chore: add untracked files — planning, openclaw module, docs, assets and data files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6168cd29c6
commit
83b2c89a1f
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
# aider chat started at 2026-03-23 12:27:25
|
||||
|
||||
> You can skip this check with --no-gitignore
|
||||
> Add .aider* to .gitignore (recommended)? (Y)es/(N)o [Yes]: y
|
||||
> Added .aider* to .gitignore
|
||||
> /home/ubuntu/.local/bin/aider --message liste os arquivos principais do projeto em uma linha cada
|
||||
> Warning: ollama/deepseek-r1:14b expects these environment variables
|
||||
> - OLLAMA_API_BASE: Not set
|
||||
> Warning for ollama/deepseek-r1:14b: Unknown context window size and costs, using sane defaults.
|
||||
> You can skip this check with --no-show-model-warnings
|
||||
> https://aider.chat/docs/llms/warnings.html
|
||||
> Open documentation url for more info? (Y)es/(N)o/(D)on't ask again [Yes]: y
|
||||
> Aider v0.86.2
|
||||
> Model: ollama/deepseek-r1:14b with diff edit format
|
||||
> Git repo: .git with 5,113 files
|
||||
> Warning: For large repos, consider using --subtree-only and .aiderignore
|
||||
> See: https://aider.chat/docs/faq.html#can-i-use-aider-in-a-large-mono-repo
|
||||
> Repo-map: using 1024 tokens, auto refresh
|
||||
|
||||
#### liste os arquivos principais do projeto em uma linha cada
|
||||
> Initial repo scan can be slow in larger repos, but only happens once.
|
||||
> litellm.APIConnectionError: OllamaException - [Errno 111] Connection refused
|
||||
> Retrying in 0.2 seconds...
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
# 2026-03-23 12:27:36.139575
|
||||
+liste os arquivos principais do projeto em uma linha cada
|
||||
|
||||
# 2026-03-23 12:27:36.139669
|
||||
+liste os arquivos principais do projeto em uma linha cada
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,27 @@
|
|||
# Arcádia Agentic Suite
|
||||
|
||||
## Vision
|
||||
|
||||
Evoluir o Arcádia Suite de ERP tradicional para um **Sistema Agêntico Orientado a Objetos**, onde Skills são objetos reutilizáveis, Agentes instanciam Skills para objetivos específicos, Automações são composições orquestradas, e o Dev Center é a fábrica de agentes.
|
||||
|
||||
## Stack
|
||||
|
||||
- **Backend:** Node.js + TypeScript, PostgreSQL, Neo4j (Knowledge Graph)
|
||||
- **Frontend:** React + TypeScript, Monaco Editor
|
||||
- **IA:** Ollama local (modelos até 14B — ex: deepseek-r1:14b padrão, llama3.1:8b rápido)
|
||||
- **BI:** Apache Superset (externo, integrado via bridge)
|
||||
- **Módulos embutidos:** MiroFlow (`server/modules/miroflow/`), OpenClaw (`server/modules/openclaw/`)
|
||||
|
||||
## Princípios
|
||||
|
||||
- Skills são o primitivo de execução universal
|
||||
- Soberania tecnológica — eliminar dependências externas para lógica de negócio
|
||||
- Multi-tenant: System → Tenant → Company → User
|
||||
- Auditoria imutável em todas as execuções
|
||||
- Backend-first em cada fase — API definida antes do frontend
|
||||
|
||||
## Non-negotiables
|
||||
|
||||
- Confirmar com João antes de executar qualquer migração de dados ou alteração de sistemas ativos
|
||||
- Nunca sobrescrever configurações existentes do Superset (RLS já configurado em produção)
|
||||
- Branch de deploy: `Servidor`
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"workflow": {
|
||||
"research": false,
|
||||
"plan_check": false,
|
||||
"verifier": false,
|
||||
"nyquist_validation": false
|
||||
},
|
||||
"model_profile": "budget"
|
||||
}
|
||||
|
|
@ -0,0 +1,329 @@
|
|||
---
|
||||
id: "03-01"
|
||||
phase: 03-miroflow-embutido
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 0
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- server/python/miroflow_service.py
|
||||
- server/python/test_miroflow_service.py
|
||||
autonomous: true
|
||||
requirements:
|
||||
- REQ-3.1
|
||||
- REQ-3.2
|
||||
- REQ-3.3
|
||||
- REQ-3.4
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "ollama pull llama3.1:8b completa sem erro"
|
||||
- "miroflow_service.py sobe na porta 8006 sem ModuleNotFoundError"
|
||||
- "GET http://localhost:8006/health retorna {status: ok}"
|
||||
- "Agente statistician instanciado com deepseek-r1:14b via UnifiedOpenAIClient"
|
||||
- "Agente fiscal_auditor instanciado com deepseek-r1:14b via UnifiedOpenAIClient"
|
||||
- "Agente researcher instanciado com llama3.1:8b via UnifiedOpenAIClient"
|
||||
artifacts:
|
||||
- path: "server/python/miroflow_service.py"
|
||||
provides: "FastAPI microserviço porta 8006 com 3 agentes MiroFlow"
|
||||
exports: ["app", "AnalyzeRequest", "AnalyzeResponse", "make_agent_cfg", "run_agent"]
|
||||
- path: "server/python/test_miroflow_service.py"
|
||||
provides: "Testes pytest para REQ-3.1 a REQ-3.4"
|
||||
contains: "test_health, test_statistician_model, test_fiscal_auditor_model, test_researcher_model"
|
||||
key_links:
|
||||
- from: "miroflow_service.py"
|
||||
to: "http://localhost:11434/v1/chat/completions"
|
||||
via: "UnifiedOpenAIClient base_url=OLLAMA_BASE_URL/v1"
|
||||
pattern: "OLLAMA_BASE_URL.*v1"
|
||||
- from: "miroflow_service.py"
|
||||
to: "server/modules/miroflow"
|
||||
via: "sys.path.insert(0, /app/server/modules/miroflow)"
|
||||
pattern: "sys.path.insert"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Preparar o ambiente e criar o microserviço Python MiroFlow (FastAPI, porta 8006) com os 3 agentes especializados configurados para Ollama local.
|
||||
|
||||
Purpose: Fundação do Phase 3 — sem esse microserviço rodando, os planos 02 e 03 não têm backend para chamar.
|
||||
Output: miroflow_service.py funcional + testes pytest cobrindo REQ-3.1 a REQ-3.4 + llama3.1:8b instalado.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/03-miroflow-embutido/03-RESEARCH.md
|
||||
|
||||
<interfaces>
|
||||
<!-- Padrão de microserviço FastAPI existente — copiar estrutura de bi_analysis_service.py -->
|
||||
<!-- server/python/bi_analysis_service.py -->
|
||||
app = FastAPI(title="...", version="1.0.0")
|
||||
app.add_middleware(CORSMiddleware, allow_origins=["*"], ...)
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check(): return {"status": "ok", "service": "..."}
|
||||
|
||||
@app.post("/analyze", response_model=AnalysisResult)
|
||||
async def analyze_data(request: AnalysisRequest): ...
|
||||
|
||||
if __name__ == "__main__":
|
||||
port = int(os.environ.get("SERVICE_PORT", 8003))
|
||||
uvicorn.run(app, host="0.0.0.0", port=port)
|
||||
|
||||
<!-- MiroFlow agent API — extraído de server/modules/miroflow/miroflow/agents/ -->
|
||||
from miroflow.agents.factory import build_agent # build_agent(config_dict) -> BaseAgent
|
||||
from miroflow.agents.context import AgentContext # AgentContext(task_description="...")
|
||||
# agent.run(ctx) -> dict com chave "summary" ou texto direto
|
||||
|
||||
<!-- Config obrigatória do LLM (campos do base.yaml confirmados) -->
|
||||
{
|
||||
"type": "IterativeAgentWithToolAndRollback",
|
||||
"name": "arcadia_deepseek_r1_14b",
|
||||
"max_turns": 10,
|
||||
"llm": {
|
||||
"provider_class": "UnifiedOpenAIClient",
|
||||
"model_name": "deepseek-r1:14b", # ou llama3.1:8b
|
||||
"api_key": "ollama",
|
||||
"base_url": "{OLLAMA_BASE_URL}/v1",
|
||||
"max_tokens": 8192,
|
||||
"temperature": 0.7,
|
||||
"top_p": 1.0, "min_p": 0.0, "top_k": -1,
|
||||
"reasoning_effort": None, "repetition_penalty": 1.0,
|
||||
"max_context_length": -1, "async_client": True,
|
||||
"disable_cache_control": True, "keep_tool_result": -1,
|
||||
"use_tool_calls": False, "oai_tool_thinking": False,
|
||||
}
|
||||
}
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Setup — baixar llama3.1:8b e instalar dependências MiroFlow</name>
|
||||
<files>server/modules/miroflow/ (sem modificar), .env (adicionar MIROFLOW_PORT)</files>
|
||||
|
||||
<read_first>
|
||||
- /opt/arcadia_merged/.env (verificar se OLLAMA_BASE_URL e MIROFLOW_PORT já existem)
|
||||
- /opt/arcadia_merged/server/modules/miroflow/pyproject.toml (confirmar deps necessárias)
|
||||
</read_first>
|
||||
|
||||
<action>
|
||||
1. Baixar o modelo llama3.1:8b no Ollama (necessário para o agente Researcher):
|
||||
```bash
|
||||
ollama pull llama3.1:8b
|
||||
```
|
||||
Se o download demorar muito, continuar e validar após. O fallback é llama3.2:3b já instalado.
|
||||
|
||||
2. Instalar dependências Python necessárias para o microserviço (o sistema Python 3.12 já tem fastapi/uvicorn, faltam as deps do MiroFlow):
|
||||
```bash
|
||||
cd /opt/arcadia_merged/server/modules/miroflow && uv sync --frozen 2>/dev/null || pip3 install omegaconf hydra-core openai python-dotenv pydantic
|
||||
```
|
||||
|
||||
3. Verificar se MIROFLOW_PORT=8006 existe no .env. Se não existir, adicionar a linha `MIROFLOW_PORT=8006` ao arquivo .env. NÃO alterar OLLAMA_BASE_URL existente.
|
||||
|
||||
4. Verificar se OLLAMA_BASE_URL está presente no .env. Se não, adicionar `OLLAMA_BASE_URL=http://localhost:11434`.
|
||||
</action>
|
||||
|
||||
<verify>
|
||||
<automated>ollama list | grep "llama3.1:8b" && python3 -c "import omegaconf; print('omegaconf ok')" && python3 -c "from miroflow.agents.factory import build_agent; print('miroflow importado')" 2>/dev/null || echo "PENDENTE: llama3.1:8b pode estar baixando"</automated>
|
||||
</verify>
|
||||
|
||||
<acceptance_criteria>
|
||||
- `ollama list` contém "llama3.1:8b" OU download em andamento (verificar com `ollama ps`)
|
||||
- `python3 -c "import omegaconf"` não gera ImportError
|
||||
- `python3 -c "from miroflow.agents.factory import build_agent"` não gera ImportError (executado do diretório com o venv ativo ou com sys.path configurado)
|
||||
- .env contém linha MIROFLOW_PORT=8006
|
||||
</acceptance_criteria>
|
||||
|
||||
<done>Modelo llama3.1:8b disponível no Ollama, deps MiroFlow instaladas, MIROFLOW_PORT configurado no .env</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Criar test_miroflow_service.py + miroflow_service.py (FastAPI porta 8006)</name>
|
||||
<files>server/python/test_miroflow_service.py, server/python/miroflow_service.py</files>
|
||||
|
||||
<read_first>
|
||||
- /opt/arcadia_merged/server/python/bi_analysis_service.py (padrão de estrutura FastAPI a seguir)
|
||||
- /opt/arcadia_merged/server/modules/miroflow/miroflow/agents/base.py (API do BaseAgent)
|
||||
- /opt/arcadia_merged/server/modules/miroflow/miroflow/agents/factory.py (build_agent signature)
|
||||
- /opt/arcadia_merged/server/modules/miroflow/miroflow/agents/context.py (AgentContext fields)
|
||||
- /opt/arcadia_merged/.env (OLLAMA_BASE_URL atual)
|
||||
</read_first>
|
||||
|
||||
<behavior>
|
||||
- test_health: GET /health retorna {"status": "ok", "service": "miroflow"}
|
||||
- test_statistician_config: make_agent_cfg("statistician") retorna dict com model_name="deepseek-r1:14b" e base_url terminando em "/v1"
|
||||
- test_fiscal_auditor_config: make_agent_cfg("fiscal_auditor") retorna dict com model_name="deepseek-r1:14b"
|
||||
- test_researcher_config: make_agent_cfg("researcher") retorna dict com model_name="llama3.1:8b" (fallback "llama3.2:3b" se llama3.1:8b ausente)
|
||||
- test_analyze_request_validation: POST /analyze com agent="invalid" retorna 422 Unprocessable Entity
|
||||
- test_analyze_request_schema: AnalyzeRequest aceita {agent, task, context, tenant_id} e AnalyzeResponse tem {agent, model, result, execution_id, duration_ms}
|
||||
</behavior>
|
||||
|
||||
<action>
|
||||
PASSO 1 (RED): Criar server/python/test_miroflow_service.py com os 6 testes listados em <behavior>. Rodar pytest — DEVE falhar (miroflow_service.py não existe).
|
||||
|
||||
PASSO 2 (GREEN): Criar server/python/miroflow_service.py seguindo o padrão de bi_analysis_service.py:
|
||||
|
||||
```python
|
||||
"""
|
||||
Arcádia MiroFlow Service — Agentes científicos via MiroFlow + Ollama local
|
||||
FastAPI microserviço porta 8006
|
||||
"""
|
||||
import os, sys, time, uuid
|
||||
from typing import Optional
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from pydantic import BaseModel
|
||||
|
||||
# Adicionar o submodule MiroFlow ao path
|
||||
MIROFLOW_MODULE_PATH = os.path.join(
|
||||
os.path.dirname(__file__), "..", "modules", "miroflow"
|
||||
)
|
||||
sys.path.insert(0, os.path.abspath(MIROFLOW_MODULE_PATH))
|
||||
|
||||
from miroflow.agents.factory import build_agent
|
||||
from miroflow.agents.context import AgentContext
|
||||
|
||||
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://localhost:11434")
|
||||
|
||||
# Verificar se llama3.1:8b está disponível; senão usar fallback
|
||||
RESEARCHER_MODEL = os.getenv("MIROFLOW_RESEARCHER_MODEL", "llama3.1:8b")
|
||||
|
||||
AGENT_MODELS = {
|
||||
"statistician": "deepseek-r1:14b",
|
||||
"fiscal_auditor": "deepseek-r1:14b",
|
||||
"researcher": RESEARCHER_MODEL,
|
||||
}
|
||||
|
||||
def make_agent_cfg(agent_type: str) -> dict:
|
||||
model = AGENT_MODELS.get(agent_type, "deepseek-r1:14b")
|
||||
return {
|
||||
"type": "IterativeAgentWithToolAndRollback",
|
||||
"name": f"arcadia_{agent_type}",
|
||||
"max_turns": 10,
|
||||
"llm": {
|
||||
"provider_class": "UnifiedOpenAIClient",
|
||||
"model_name": model,
|
||||
"api_key": "ollama",
|
||||
"base_url": f"{OLLAMA_BASE_URL}/v1",
|
||||
"max_tokens": 8192,
|
||||
"temperature": 0.7,
|
||||
"top_p": 1.0, "min_p": 0.0, "top_k": -1,
|
||||
"reasoning_effort": None, "repetition_penalty": 1.0,
|
||||
"max_context_length": -1, "async_client": True,
|
||||
"disable_cache_control": True, "keep_tool_result": -1,
|
||||
"use_tool_calls": False, "oai_tool_thinking": False,
|
||||
},
|
||||
}
|
||||
|
||||
async def run_agent(agent_type: str, task: str) -> tuple[str, str]:
|
||||
"""Retorna (result_text, model_name)"""
|
||||
if agent_type not in AGENT_MODELS:
|
||||
raise ValueError(f"Agente desconhecido: {agent_type}. Use: {list(AGENT_MODELS.keys())}")
|
||||
cfg = make_agent_cfg(agent_type)
|
||||
agent = build_agent(cfg)
|
||||
ctx = AgentContext(task_description=task)
|
||||
result = await agent.run(ctx)
|
||||
text = result.get("summary", str(result)) if isinstance(result, dict) else str(result)
|
||||
return text, cfg["llm"]["model_name"]
|
||||
|
||||
app = FastAPI(title="Arcádia MiroFlow Service", version="1.0.0")
|
||||
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True,
|
||||
allow_methods=["*"], allow_headers=["*"])
|
||||
|
||||
class AnalyzeRequest(BaseModel):
|
||||
agent: str # "statistician" | "fiscal_auditor" | "researcher"
|
||||
task: str
|
||||
context: dict = {}
|
||||
tenant_id: Optional[int] = None
|
||||
|
||||
class AnalyzeResponse(BaseModel):
|
||||
agent: str
|
||||
model: str
|
||||
result: str
|
||||
execution_id: str
|
||||
duration_ms: int
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
return {"status": "ok", "service": "miroflow", "agents": list(AGENT_MODELS.keys())}
|
||||
|
||||
@app.post("/analyze", response_model=AnalyzeResponse)
|
||||
async def analyze(req: AnalyzeRequest):
|
||||
if req.agent not in AGENT_MODELS:
|
||||
raise HTTPException(status_code=422, detail=f"Agente inválido: {req.agent}")
|
||||
start = time.time()
|
||||
try:
|
||||
result_text, model_name = await run_agent(req.agent, req.task)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
return AnalyzeResponse(
|
||||
agent=req.agent,
|
||||
model=model_name,
|
||||
result=result_text,
|
||||
execution_id=str(uuid.uuid4()),
|
||||
duration_ms=int((time.time() - start) * 1000),
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
port = int(os.environ.get("MIROFLOW_PORT", 8006))
|
||||
uvicorn.run(app, host="0.0.0.0", port=port)
|
||||
```
|
||||
|
||||
PASSO 3 (REFACTOR): Rodar pytest novamente — TODOS devem passar. Não alterar lógica, apenas organizar se necessário.
|
||||
|
||||
IMPORTANTE: Não usar modelos acima de 14B. Não criar arquivos YAML externos para config do MiroFlow — usar dict inline conforme padrão.
|
||||
</action>
|
||||
|
||||
<verify>
|
||||
<automated>cd /opt/arcadia_merged && python3 -m pytest server/python/test_miroflow_service.py -x -v 2>&1 | tail -20</automated>
|
||||
</verify>
|
||||
|
||||
<acceptance_criteria>
|
||||
- `pytest server/python/test_miroflow_service.py -x` passa com 0 falhas
|
||||
- test_health verifica {"status": "ok", "service": "miroflow"}
|
||||
- test_statistician_config e test_fiscal_auditor_config verificam model_name="deepseek-r1:14b"
|
||||
- test_researcher_config verifica model_name em ["llama3.1:8b", "llama3.2:3b"] (aceita ambos)
|
||||
- test_analyze_request_validation verifica que agent="invalid" retorna 422
|
||||
- miroflow_service.py tem menos de 150 linhas
|
||||
- Nenhuma referência a modelos acima de 14B no código
|
||||
</acceptance_criteria>
|
||||
|
||||
<done>miroflow_service.py criado e testado. Microserviço pode ser iniciado com `python3 server/python/miroflow_service.py` na porta 8006. Todos os testes passam.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
```bash
|
||||
# Verificar que o serviço sobe:
|
||||
cd /opt/arcadia_merged && MIROFLOW_PORT=8006 python3 server/python/miroflow_service.py &
|
||||
sleep 3
|
||||
curl -s http://localhost:8006/health | python3 -m json.tool
|
||||
# Esperado: {"status": "ok", "service": "miroflow", "agents": ["statistician", "fiscal_auditor", "researcher"]}
|
||||
kill %1
|
||||
```
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- `pytest server/python/test_miroflow_service.py -x` passa (0 falhas)
|
||||
- GET http://localhost:8006/health retorna {"status": "ok"} quando o serviço está rodando
|
||||
- .env contém MIROFLOW_PORT=8006
|
||||
- llama3.1:8b disponível no Ollama (ou download iniciado)
|
||||
- Nenhum modelo acima de 14B referenciado
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Após conclusão, criar `.planning/phases/03-miroflow-embutido/03-01-SUMMARY.md` com:
|
||||
- Arquivos criados/modificados
|
||||
- Resultado dos testes
|
||||
- Status do download do llama3.1:8b
|
||||
- Modelo de fallback usado para Researcher (se aplicável)
|
||||
</output>
|
||||
|
|
@ -0,0 +1,331 @@
|
|||
---
|
||||
id: "03-02"
|
||||
phase: 03-miroflow-embutido
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: ["03-01"]
|
||||
files_modified:
|
||||
- server/miroflow/routes.ts
|
||||
- server/miroflow/engine-proxy.ts
|
||||
- server/routes.ts
|
||||
autonomous: true
|
||||
requirements:
|
||||
- REQ-3.5
|
||||
- REQ-3.7
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "POST /api/miroflow/analyze retorna JSON com {agent, model, result, execution_id, duration_ms}"
|
||||
- "GET /api/miroflow/health retorna {online: true} quando microserviço está rodando"
|
||||
- "Requisição não autenticada retorna 401"
|
||||
- "Cada execução cria um nó graph_nodes com type='miroflow_execution' e auditHash SHA-256"
|
||||
- "Timeout de 5 minutos no proxy (adequado para deepseek-r1:14b)"
|
||||
artifacts:
|
||||
- path: "server/miroflow/engine-proxy.ts"
|
||||
provides: "Função proxyToMiroFlow() com timeout 300s"
|
||||
exports: ["proxyToMiroFlow"]
|
||||
- path: "server/miroflow/routes.ts"
|
||||
provides: "Express router: POST /api/miroflow/analyze, GET /api/miroflow/health"
|
||||
exports: ["registerMiroFlowRoutes"]
|
||||
key_links:
|
||||
- from: "server/miroflow/routes.ts"
|
||||
to: "server/python/miroflow_service.py"
|
||||
via: "fetch() para http://localhost:8006/analyze"
|
||||
pattern: "MIROFLOW_PORT.*8006"
|
||||
- from: "server/miroflow/routes.ts"
|
||||
to: "server/graph/service.ts"
|
||||
via: "createNode({type: 'miroflow_execution', ...})"
|
||||
pattern: "createNode.*miroflow_execution"
|
||||
- from: "server/routes.ts"
|
||||
to: "server/miroflow/routes.ts"
|
||||
via: "registerMiroFlowRoutes(app)"
|
||||
pattern: "registerMiroFlowRoutes"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Criar o bridge Node.js → Python para o MiroFlow: proxy TypeScript (engine-proxy.ts), rotas Express (routes.ts) e registro imutável de execuções no KG via graph_nodes.
|
||||
|
||||
Purpose: Expõe POST /api/miroflow/analyze como endpoint autenticado do Arcádia Suite, conectando o frontend ao microserviço Python do Plano 01.
|
||||
Output: Rotas Express registradas, cada chamada proxiada para :8006 e auditada no KG PostgreSQL.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/phases/03-miroflow-embutido/03-01-SUMMARY.md
|
||||
@.planning/phases/03-miroflow-embutido/03-RESEARCH.md
|
||||
|
||||
<interfaces>
|
||||
<!-- Padrão de proxy existente — server/bi/engine-proxy.ts -->
|
||||
// TIMEOUT padrão atual: 30000ms — MiroFlow PRECISA de 300000ms (5min)
|
||||
async function proxyToEngine(path: string, options: RequestInit = {}): Promise<any> {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), BI_ENGINE_TIMEOUT);
|
||||
const response = await fetch(`${BI_ENGINE_URL}${path}`, { ...options, signal: controller.signal });
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({ detail: response.statusText }));
|
||||
throw new Error(error.detail || `BI Engine error: ${response.status}`);
|
||||
}
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
export function registerBiEngineRoutes(app: Express): void {
|
||||
app.post("/api/bi-engine/analyze", async (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.status(401).json({ error: "Not authenticated" });
|
||||
...
|
||||
});
|
||||
}
|
||||
|
||||
<!-- KG service — server/graph/service.ts -->
|
||||
export async function createNode(data: InsertGraphNode): Promise<GraphNode>
|
||||
// InsertGraphNode: { type: string, tenantId?: number, data: object }
|
||||
// graph_nodes tabela PostgreSQL — NÃO usar Neo4j direto
|
||||
|
||||
<!-- Registro de routes em server/routes.ts (linha ~105) -->
|
||||
import { registerBiEngineRoutes } from "./bi/engine-proxy"; // padrão a seguir
|
||||
registerBiEngineRoutes(app); // linha ~105
|
||||
|
||||
<!-- AnalyzeResponse do microserviço Python (03-01) -->
|
||||
{
|
||||
agent: string, // "statistician" | "fiscal_auditor" | "researcher"
|
||||
model: string, // "deepseek-r1:14b" | "llama3.1:8b"
|
||||
result: string,
|
||||
execution_id: string, // UUID v4
|
||||
duration_ms: number
|
||||
}
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Criar server/miroflow/engine-proxy.ts e server/miroflow/routes.ts</name>
|
||||
<files>server/miroflow/engine-proxy.ts, server/miroflow/routes.ts</files>
|
||||
|
||||
<read_first>
|
||||
- /opt/arcadia_merged/server/bi/engine-proxy.ts (padrão completo de proxy a seguir)
|
||||
- /opt/arcadia_merged/server/graph/service.ts (createNode signature, linhas 41-49)
|
||||
- /opt/arcadia_merged/server/graph/routes.ts (confirmar que /api/graph/nodes aceita POST)
|
||||
- /opt/arcadia_merged/server/auth.ts (confirmar req.isAuthenticated() e req.user shape)
|
||||
</read_first>
|
||||
|
||||
<behavior>
|
||||
- proxyToMiroFlow("/health", "GET") faz fetch para MIROFLOW_URL/health com timeout 5s
|
||||
- proxyToMiroFlow("/analyze", "POST", body) faz fetch para MIROFLOW_URL/analyze com timeout 300000ms
|
||||
- POST /api/miroflow/analyze sem autenticação retorna 401 {"error": "Não autenticado"}
|
||||
- POST /api/miroflow/analyze autenticado encaminha body + tenant_id para :8006/analyze
|
||||
- Após resposta bem-sucedida, registerExecutionInKG cria nó com type="miroflow_execution"
|
||||
- auditHash é SHA-256 do JSON.stringify({execution_id, agent, model, input, output})
|
||||
- GET /api/miroflow/health não requer autenticação, retorna {online: bool, url: string}
|
||||
</behavior>
|
||||
|
||||
<action>
|
||||
Criar diretório server/miroflow/ se não existir.
|
||||
|
||||
Criar server/miroflow/engine-proxy.ts:
|
||||
```typescript
|
||||
import type { Express, Request, Response } from "express";
|
||||
import crypto from "crypto";
|
||||
import { createNode } from "../graph/service";
|
||||
|
||||
const MIROFLOW_HOST = process.env.MIROFLOW_HOST || "localhost";
|
||||
const MIROFLOW_PORT = parseInt(process.env.MIROFLOW_PORT || "8006", 10);
|
||||
const MIROFLOW_URL = `http://${MIROFLOW_HOST}:${MIROFLOW_PORT}`;
|
||||
const MIROFLOW_TIMEOUT = 300_000; // 5 minutos — deepseek-r1:14b pode levar 60-120s
|
||||
|
||||
async function proxyToMiroFlow(path: string, method: string = "GET", body?: object): Promise<any> {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), MIROFLOW_TIMEOUT);
|
||||
try {
|
||||
const response = await fetch(`${MIROFLOW_URL}${path}`, {
|
||||
method,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
signal: controller.signal,
|
||||
});
|
||||
clearTimeout(timeout);
|
||||
if (!response.ok) {
|
||||
const err = await response.json().catch(() => ({ detail: response.statusText }));
|
||||
throw new Error(err.detail || `MiroFlow error: ${response.status}`);
|
||||
}
|
||||
return await response.json();
|
||||
} catch (err: any) {
|
||||
clearTimeout(timeout);
|
||||
if (err.name === "AbortError") throw new Error("MiroFlow timeout (300s)");
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async function registerExecutionInKG(req: Request, execData: any, inputBody: any): Promise<void> {
|
||||
try {
|
||||
const auditHash = crypto
|
||||
.createHash("sha256")
|
||||
.update(JSON.stringify({
|
||||
execution_id: execData.execution_id,
|
||||
agent: execData.agent,
|
||||
model: execData.model,
|
||||
input: inputBody,
|
||||
output: execData.result,
|
||||
}))
|
||||
.digest("hex");
|
||||
|
||||
await createNode({
|
||||
type: "miroflow_execution",
|
||||
tenantId: (req.user as any)?.tenantId ?? null,
|
||||
data: {
|
||||
...execData,
|
||||
input: inputBody,
|
||||
auditHash,
|
||||
immutable: true,
|
||||
registeredAt: new Date().toISOString(),
|
||||
},
|
||||
});
|
||||
} catch (kgErr: any) {
|
||||
// KG failure não deve bloquear a resposta ao cliente
|
||||
console.error("[MiroFlow] Falha ao registrar no KG:", kgErr.message);
|
||||
}
|
||||
}
|
||||
|
||||
export function registerMiroFlowRoutes(app: Express): void {
|
||||
app.get("/api/miroflow/health", async (_req: Request, res: Response) => {
|
||||
try {
|
||||
const data = await proxyToMiroFlow("/health", "GET");
|
||||
res.json({ online: true, url: MIROFLOW_URL, ...data });
|
||||
} catch {
|
||||
res.json({ online: false, url: MIROFLOW_URL });
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/api/miroflow/analyze", async (req: Request, res: Response) => {
|
||||
if (!req.isAuthenticated()) {
|
||||
return res.status(401).json({ error: "Não autenticado" });
|
||||
}
|
||||
try {
|
||||
const inputBody = {
|
||||
...req.body,
|
||||
tenant_id: (req.user as any)?.tenantId ?? null,
|
||||
};
|
||||
const data = await proxyToMiroFlow("/analyze", "POST", inputBody);
|
||||
// Registrar no KG de forma assíncrona (não bloqueia a resposta)
|
||||
registerExecutionInKG(req, data, req.body).catch(() => {});
|
||||
res.json(data);
|
||||
} catch (err: any) {
|
||||
res.status(502).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`[MiroFlow Proxy] Rotas registradas -> ${MIROFLOW_URL}`);
|
||||
}
|
||||
```
|
||||
|
||||
Criar server/miroflow/routes.ts como re-export limpo (manter compatibilidade com o loader de módulos):
|
||||
```typescript
|
||||
export { registerMiroFlowRoutes } from "./engine-proxy";
|
||||
```
|
||||
|
||||
IMPORTANTE:
|
||||
- Timeout de 300_000ms (5 minutos) — NÃO usar o padrão de 30s do bi/engine-proxy.ts
|
||||
- registerExecutionInKG usa createNode do graph/service.ts, NÃO faz fetch para /api/graph/nodes
|
||||
- Falha no KG não retorna erro ao cliente (try/catch com log)
|
||||
- auditHash cobre: execution_id + agent + model + input + output
|
||||
</action>
|
||||
|
||||
<verify>
|
||||
<automated>cd /opt/arcadia_merged && npx tsc --noEmit --project tsconfig.json 2>&1 | grep -i "miroflow" | head -10 || echo "TypeScript OK (nenhum erro em miroflow)"</automated>
|
||||
</verify>
|
||||
|
||||
<acceptance_criteria>
|
||||
- server/miroflow/engine-proxy.ts existe e compila sem erros TypeScript
|
||||
- server/miroflow/routes.ts existe (re-export ou implementação direta)
|
||||
- MIROFLOW_TIMEOUT = 300_000 está definido (não 30000)
|
||||
- Função registerExecutionInKG usa createNode importado de "../graph/service"
|
||||
- auditHash calculado com crypto.createHash("sha256")
|
||||
- POST /api/miroflow/analyze faz verificação req.isAuthenticated() antes de proxiar
|
||||
- grep -n "300_000\|300000" server/miroflow/engine-proxy.ts retorna resultado
|
||||
- grep -n "miroflow_execution" server/miroflow/engine-proxy.ts retorna resultado
|
||||
</acceptance_criteria>
|
||||
|
||||
<done>Arquivos TypeScript criados sem erros de compilação. Funções exportadas: registerMiroFlowRoutes. Timeout de 5min configurado. KG logging com SHA-256 implementado.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Registrar MiroFlow em server/routes.ts</name>
|
||||
<files>server/routes.ts</files>
|
||||
|
||||
<read_first>
|
||||
- /opt/arcadia_merged/server/routes.ts (ARQUIVO INTEIRO — verificar imports e onde registerBiEngineRoutes é chamado para inserir MiroFlow no mesmo padrão)
|
||||
</read_first>
|
||||
|
||||
<action>
|
||||
Adicionar import e registro das rotas MiroFlow em server/routes.ts, seguindo o padrão exato de registerBiEngineRoutes:
|
||||
|
||||
1. Adicionar import após a linha que importa registerBiEngineRoutes (linha ~19):
|
||||
```typescript
|
||||
import { registerMiroFlowRoutes } from "./miroflow/engine-proxy";
|
||||
```
|
||||
|
||||
2. Adicionar chamada após registerBiEngineRoutes(app) (linha ~105):
|
||||
```typescript
|
||||
registerMiroFlowRoutes(app);
|
||||
```
|
||||
|
||||
NÃO alterar nenhuma outra linha do arquivo. NÃO mover, reordenar ou reformatar código existente.
|
||||
NÃO modificar configurações do Superset ou rotas existentes.
|
||||
</action>
|
||||
|
||||
<verify>
|
||||
<automated>cd /opt/arcadia_merged && grep -n "registerMiroFlowRoutes" server/routes.ts && npx tsc --noEmit --project tsconfig.json 2>&1 | grep -c "error" || echo "0 erros"</automated>
|
||||
</verify>
|
||||
|
||||
<acceptance_criteria>
|
||||
- `grep "registerMiroFlowRoutes" server/routes.ts` retorna 2 linhas (import + chamada)
|
||||
- `npx tsc --noEmit` não retorna erros relacionados a miroflow
|
||||
- A linha de import está próxima de registerBiEngineRoutes (mesma região do arquivo)
|
||||
- A chamada registerMiroFlowRoutes(app) está próxima de registerBiEngineRoutes(app)
|
||||
- Nenhuma linha existente do arquivo foi modificada (verificar com git diff)
|
||||
</acceptance_criteria>
|
||||
|
||||
<done>server/routes.ts registra as rotas MiroFlow. POST /api/miroflow/analyze e GET /api/miroflow/health estarão disponíveis quando o servidor Node iniciar.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
```bash
|
||||
# Compilação TypeScript limpa:
|
||||
cd /opt/arcadia_merged && npx tsc --noEmit 2>&1 | grep -i "error" | head -5
|
||||
|
||||
# Confirmar registro no KG:
|
||||
grep -n "miroflow_execution\|auditHash\|createNode" /opt/arcadia_merged/server/miroflow/engine-proxy.ts
|
||||
|
||||
# Confirmar timeout correto:
|
||||
grep -n "300" /opt/arcadia_merged/server/miroflow/engine-proxy.ts
|
||||
|
||||
# Confirmar rotas registradas:
|
||||
grep -n "registerMiroFlowRoutes" /opt/arcadia_merged/server/routes.ts
|
||||
```
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- server/miroflow/engine-proxy.ts e server/miroflow/routes.ts existem
|
||||
- TypeScript compila sem erros nos novos arquivos
|
||||
- server/routes.ts inclui import e chamada registerMiroFlowRoutes
|
||||
- Timeout de 300s configurado no proxy
|
||||
- auditHash SHA-256 calculado e salvo via createNode
|
||||
- GET /api/miroflow/health acessível sem autenticação
|
||||
- POST /api/miroflow/analyze requer autenticação (401 sem sessão)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Após conclusão, criar `.planning/phases/03-miroflow-embutido/03-02-SUMMARY.md` com:
|
||||
- Arquivos criados/modificados
|
||||
- Resultado da compilação TypeScript
|
||||
- Confirmação do padrão de timeout (300s)
|
||||
- Confirmação do KG logging com auditHash
|
||||
</output>
|
||||
|
|
@ -0,0 +1,315 @@
|
|||
---
|
||||
id: "03-03"
|
||||
phase: 03-miroflow-embutido
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: ["03-02"]
|
||||
files_modified:
|
||||
- client/src/components/MiroFlowControl.tsx
|
||||
- client/src/pages/BiWorkspace.tsx
|
||||
autonomous: false
|
||||
requirements:
|
||||
- REQ-3.6
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "Tab 'Científico' aparece na TabsList de BiWorkspace.tsx"
|
||||
- "MiroFlowControl.tsx renderiza toggle 'Modo Científico' com ícone Brain"
|
||||
- "Usuário pode selecionar agente (Statistician / Fiscal Auditor / Researcher)"
|
||||
- "Usuário pode digitar tarefa e clicar em Analisar"
|
||||
- "Resultado da análise é exibido em área de texto com scroll"
|
||||
- "Estado de loading é exibido durante a chamada (ícone Loader2 girando)"
|
||||
- "Erro da API é exibido ao usuário de forma legível"
|
||||
- "Tabs existentes (overview, datasources, etc.) continuam funcionando normalmente"
|
||||
artifacts:
|
||||
- path: "client/src/components/MiroFlowControl.tsx"
|
||||
provides: "Componente React com toggle, seletor de agente, input de tarefa e exibição de resultado"
|
||||
exports: ["MiroFlowControl"]
|
||||
- path: "client/src/pages/BiWorkspace.tsx"
|
||||
provides: "Tab 'cientifico' adicionada à TabsList e TabsContent existentes"
|
||||
contains: "TabsTrigger value=\"cientifico\""
|
||||
key_links:
|
||||
- from: "client/src/components/MiroFlowControl.tsx"
|
||||
to: "/api/miroflow/analyze"
|
||||
via: "apiRequest('POST', '/api/miroflow/analyze', {agent, task})"
|
||||
pattern: "miroflow/analyze"
|
||||
- from: "client/src/pages/BiWorkspace.tsx"
|
||||
to: "client/src/components/MiroFlowControl.tsx"
|
||||
via: "import { MiroFlowControl } from '@/components/MiroFlowControl'"
|
||||
pattern: "MiroFlowControl"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Criar o componente MiroFlowControl.tsx e integrá-lo ao BiWorkspace.tsx como nova tab "Científico" com toggle "Modo Científico".
|
||||
|
||||
Purpose: Expõe as análises científicas MiroFlow diretamente no contexto BI, permitindo ao usuário acionar agentes LLM especializados ao lado dos dashboards Superset.
|
||||
Output: Tab funcional em BiWorkspace com formulário de análise, seletor de agente e display de resultado.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/phases/03-miroflow-embutido/03-02-SUMMARY.md
|
||||
@.planning/phases/03-miroflow-embutido/03-RESEARCH.md
|
||||
|
||||
<interfaces>
|
||||
<!-- BiWorkspace.tsx — estrutura de tabs (linhas 2870-2888) -->
|
||||
// Estado:
|
||||
const [activeTab, setActiveTab] = useState("overview");
|
||||
|
||||
// Estrutura:
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-6">
|
||||
<TabsList className="bg-[#1f334d] border border-[#c89b3c]/20 p1">
|
||||
<TabsTrigger value="overview" className="data-[state=active]:bg-[#c89b3c] data-[state=active]:text-[#1f334d] text-white/70">
|
||||
<LayoutDashboard className="w-4 h-4 mr-2" /> Visão Geral
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="datasources" ...> <Database .../> Fontes de Dados </TabsTrigger>
|
||||
<TabsTrigger value="upload" ...> <Upload .../> Upload </TabsTrigger>
|
||||
<TabsTrigger value="datasets" ...> <FileText .../> Consultas </TabsTrigger>
|
||||
<TabsTrigger value="charts" ...> <BarChart3 .../> Gráficos </TabsTrigger>
|
||||
<TabsTrigger value="backups" ...> <Archive .../> Backups </TabsTrigger>
|
||||
<!-- ADICIONAR AQUI: <TabsTrigger value="cientifico" ...> <Brain .../> Científico </TabsTrigger> -->
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="overview">...</TabsContent>
|
||||
<TabsContent value="datasources">...</TabsContent>
|
||||
<!-- ... outros TabsContent ... -->
|
||||
<!-- ADICIONAR: <TabsContent value="cientifico"><MiroFlowControl /></TabsContent> -->
|
||||
</Tabs>
|
||||
|
||||
<!-- Imports disponíveis em BiWorkspace.tsx — já importados, usar estes -->
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
// Lucide icons já importados: Bot, Brain NÃO está importado — adicionar ao import
|
||||
// shadcn/ui components já disponíveis: Button, Card, CardContent, CardHeader, CardTitle,
|
||||
// Badge, ScrollArea, Input, Textarea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue
|
||||
|
||||
<!-- API pattern usado no projeto (apiRequest de wouter/queryClient) -->
|
||||
// Verificar como outros componentes fazem POST — geralmente:
|
||||
import { apiRequest } from "@/lib/queryClient";
|
||||
// ou via useMutation do @tanstack/react-query
|
||||
const mutation = useMutation({ mutationFn: (body) => apiRequest("POST", "/api/miroflow/analyze", body) });
|
||||
|
||||
<!-- AnalyzeResponse do backend (03-02) -->
|
||||
interface AnalyzeResponse {
|
||||
agent: string;
|
||||
model: string;
|
||||
result: string;
|
||||
execution_id: string;
|
||||
duration_ms: number;
|
||||
}
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Criar client/src/components/MiroFlowControl.tsx</name>
|
||||
<files>client/src/components/MiroFlowControl.tsx</files>
|
||||
|
||||
<read_first>
|
||||
- /opt/arcadia_merged/client/src/lib/queryClient.ts (confirmar apiRequest signature)
|
||||
- /opt/arcadia_merged/client/src/pages/BiWorkspace.tsx linhas 1-60 (imports disponíveis e padrão de componentes)
|
||||
- /opt/arcadia_merged/client/src/components/ (listar arquivos para verificar convenções de nomenclatura)
|
||||
</read_first>
|
||||
|
||||
<action>
|
||||
Criar client/src/components/MiroFlowControl.tsx como componente React standalone.
|
||||
|
||||
O componente deve:
|
||||
1. Exibir card com header "Modo Científico" e ícone Brain
|
||||
2. Toggle switch ou badge "Ativado" quando em modo científico (estado local `enabled`)
|
||||
3. Select para escolha do agente: Statistician (deepseek-r1:14b), Fiscal Auditor (deepseek-r1:14b), Researcher (llama3.1:8b)
|
||||
4. Textarea para digitar a tarefa (placeholder específico por agente)
|
||||
5. Botão "Analisar" que dispara POST /api/miroflow/analyze
|
||||
6. Área de resultado com ScrollArea mostrando a resposta formatada
|
||||
7. Estado de loading com Loader2 animado durante chamada
|
||||
8. Exibir erros de forma legível (badge vermelho + mensagem)
|
||||
9. Metadados do resultado: modelo usado, tempo de execução, execution_id (em texto pequeno)
|
||||
|
||||
Props: `currentDashboardData?: Record<string, unknown>` (opcional, para contexto futuro)
|
||||
|
||||
Usar useMutation do @tanstack/react-query para a chamada POST.
|
||||
|
||||
Estrutura de tipos locais:
|
||||
```typescript
|
||||
interface AnalyzeRequest {
|
||||
agent: "statistician" | "fiscal_auditor" | "researcher";
|
||||
task: string;
|
||||
context?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface AnalyzeResponse {
|
||||
agent: string;
|
||||
model: string;
|
||||
result: string;
|
||||
execution_id: string;
|
||||
duration_ms: number;
|
||||
}
|
||||
```
|
||||
|
||||
Agentes disponíveis (hardcoded, sem fetch):
|
||||
```typescript
|
||||
const AGENTS = [
|
||||
{ value: "statistician", label: "Statistician", model: "deepseek-r1:14b",
|
||||
placeholder: "Ex: Analise a distribuição de vendas por região no último trimestre" },
|
||||
{ value: "fiscal_auditor", label: "Fiscal Auditor", model: "deepseek-r1:14b",
|
||||
placeholder: "Ex: Verifique inconsistências nos registros NFe do CNPJ 12.345.678/0001-90" },
|
||||
{ value: "researcher", label: "Researcher", model: "llama3.1:8b",
|
||||
placeholder: "Ex: Quais fornecedores têm maior correlação com atrasos de entrega?" },
|
||||
] as const;
|
||||
```
|
||||
|
||||
Estilização: seguir o padrão dark de BiWorkspace (bg-[#1f334d], text-white, border-[#c89b3c]/20, text-[#c89b3c]).
|
||||
|
||||
NÃO usar estados globais, stores ou contextos externos.
|
||||
NÃO fazer fetch para /api/miroflow/health neste componente.
|
||||
</action>
|
||||
|
||||
<verify>
|
||||
<automated>cd /opt/arcadia_merged && npx tsc --noEmit --project tsconfig.json 2>&1 | grep -i "MiroFlowControl\|miroflow" | head -10 || echo "TypeScript OK"</automated>
|
||||
</verify>
|
||||
|
||||
<acceptance_criteria>
|
||||
- client/src/components/MiroFlowControl.tsx existe e exporta MiroFlowControl como default ou named export
|
||||
- `npx tsc --noEmit` não retorna erros relacionados a MiroFlowControl.tsx
|
||||
- Componente tem prop `currentDashboardData?: Record<string, unknown>`
|
||||
- Select com os 3 agentes (statistician, fiscal_auditor, researcher)
|
||||
- Textarea para task com placeholder dinâmico por agente selecionado
|
||||
- useMutation configurado para POST /api/miroflow/analyze
|
||||
- Estado de loading exibido (Loader2 ou spinner equivalente)
|
||||
- Erro exibido quando mutation.isError
|
||||
- Resultado exibido quando mutation.isSuccess com metadados (model, duration_ms)
|
||||
- Nenhuma referência a modelos acima de 14B
|
||||
</acceptance_criteria>
|
||||
|
||||
<done>MiroFlowControl.tsx criado, compila sem erros TypeScript. Componente pronto para ser importado pelo BiWorkspace.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Adicionar tab "Científico" ao BiWorkspace.tsx</name>
|
||||
<files>client/src/pages/BiWorkspace.tsx</files>
|
||||
|
||||
<read_first>
|
||||
- /opt/arcadia_merged/client/src/pages/BiWorkspace.tsx linhas 2860-2920 (TabsList atual — localizar exatamente onde inserir)
|
||||
- /opt/arcadia_merged/client/src/pages/BiWorkspace.tsx linhas 1-55 (imports para adicionar Brain e MiroFlowControl)
|
||||
- /opt/arcadia_merged/client/src/pages/BiWorkspace.tsx linhas 2950-3050 (após os TabsContent existentes — localizar o fechamento para inserir novo TabsContent)
|
||||
</read_first>
|
||||
|
||||
<action>
|
||||
BiWorkspace.tsx é um arquivo grande (~3000 linhas). Fazer APENAS as 3 modificações cirúrgicas abaixo:
|
||||
|
||||
MODIFICAÇÃO 1 — Adicionar Brain ao import de lucide-react (linha ~10-50):
|
||||
Localizar `import { ... } from "lucide-react"` e adicionar `Brain` à lista.
|
||||
(Verificar se Brain já está importado antes de adicionar)
|
||||
|
||||
MODIFICAÇÃO 2 — Adicionar MiroFlowControl import (após os imports existentes):
|
||||
```typescript
|
||||
import { MiroFlowControl } from "@/components/MiroFlowControl";
|
||||
```
|
||||
|
||||
MODIFICAÇÃO 3 — Adicionar TabsTrigger na TabsList (após o último TabsTrigger existente, antes do fechamento </TabsList>):
|
||||
```tsx
|
||||
<TabsTrigger value="cientifico" className="data-[state=active]:bg-[#c89b3c] data-[state=active]:text-[#1f334d] text-white/70">
|
||||
<Brain className="w-4 h-4 mr-2" /> Científico
|
||||
</TabsTrigger>
|
||||
```
|
||||
|
||||
MODIFICAÇÃO 4 — Adicionar TabsContent (após o último TabsContent existente, antes do fechamento </Tabs>):
|
||||
```tsx
|
||||
<TabsContent value="cientifico">
|
||||
<MiroFlowControl />
|
||||
</TabsContent>
|
||||
```
|
||||
|
||||
NÃO alterar nenhum TabsTrigger ou TabsContent existente.
|
||||
NÃO modificar o estado activeTab ou qualquer lógica existente.
|
||||
NÃO reformatar ou reorganizar o código existente.
|
||||
NÃO remover nenhum import existente.
|
||||
Verificar o arquivo antes e depois com grep para confirmar que os 4 tabs originais ainda existem.
|
||||
</action>
|
||||
|
||||
<verify>
|
||||
<automated>cd /opt/arcadia_merged && grep -c 'TabsTrigger value=' client/src/pages/BiWorkspace.tsx && npx tsc --noEmit --project tsconfig.json 2>&1 | grep -i "BiWorkspace\|MiroFlow" | head -5 || echo "TypeScript OK"</automated>
|
||||
</verify>
|
||||
|
||||
<acceptance_criteria>
|
||||
- `grep -c 'TabsTrigger value=' client/src/pages/BiWorkspace.tsx` retorna 7 (6 existentes + 1 novo "cientifico")
|
||||
- `grep 'value="cientifico"' client/src/pages/BiWorkspace.tsx` retorna 2 linhas (TabsTrigger + TabsContent)
|
||||
- `grep 'Brain' client/src/pages/BiWorkspace.tsx` retorna resultado (importado e usado)
|
||||
- `grep 'MiroFlowControl' client/src/pages/BiWorkspace.tsx` retorna resultado (importado e usado)
|
||||
- `npx tsc --noEmit` não retorna erros em BiWorkspace.tsx
|
||||
- Tabs existentes ("overview", "datasources", "upload", "datasets", "charts", "backups") ainda presentes
|
||||
- Nenhum outro código do arquivo foi alterado (git diff mostra apenas as 4 adições)
|
||||
</acceptance_criteria>
|
||||
|
||||
<done>BiWorkspace.tsx contém tab "Científico" que renderiza MiroFlowControl. Tabs existentes preservadas. Compila sem erros.</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>
|
||||
- MiroFlowControl.tsx: componente com toggle "Modo Científico", seletor de 3 agentes, textarea de tarefa e display de resultado
|
||||
- BiWorkspace.tsx: tab "Científico" adicionada (7 tabs no total)
|
||||
- Backend (planos 01 e 02): microserviço Python porta 8006 + proxy Node + KG logging
|
||||
</what-built>
|
||||
|
||||
<how-to-verify>
|
||||
1. Iniciar o servidor: `npm run dev` (ou verificar se já está rodando)
|
||||
2. Iniciar o microserviço MiroFlow: `MIROFLOW_PORT=8006 python3 server/python/miroflow_service.py &`
|
||||
3. Navegar para /bi-workspace no browser
|
||||
4. Verificar que a tab "Científico" aparece no TabsList (ícone Brain + texto)
|
||||
5. Clicar na tab "Científico"
|
||||
6. Verificar que o painel MiroFlowControl renderiza sem erros JavaScript no console
|
||||
7. Selecionar o agente "Statistician"
|
||||
8. Digitar uma tarefa simples: "Liste os 3 principais insights sobre análise estatística"
|
||||
9. Clicar em "Analisar"
|
||||
10. Verificar que o estado de loading aparece (Loader2 girando)
|
||||
11. Após resposta: verificar que o resultado é exibido com o modelo (deepseek-r1:14b) e tempo de execução
|
||||
12. Verificar GET http://localhost:8006/health retorna {"online": true} em outro aba
|
||||
13. Verificar as outras 6 tabs (overview, datasources, etc.) ainda funcionam normalmente
|
||||
</how-to-verify>
|
||||
|
||||
<resume-signal>
|
||||
Digite "aprovado" se tudo funcionar corretamente.
|
||||
Ou descreva os problemas encontrados (ex: "tab não aparece", "erro 502 na análise", "loading infinito").
|
||||
</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
```bash
|
||||
# Verificar estrutura dos arquivos:
|
||||
ls /opt/arcadia_merged/client/src/components/MiroFlowControl.tsx
|
||||
|
||||
# Verificar tab adicionada:
|
||||
grep -n 'cientifico\|Científico\|MiroFlowControl\|Brain' /opt/arcadia_merged/client/src/pages/BiWorkspace.tsx
|
||||
|
||||
# Verificar compilação:
|
||||
cd /opt/arcadia_merged && npx tsc --noEmit 2>&1 | tail -5
|
||||
|
||||
# Contagem de tabs:
|
||||
grep -c 'TabsTrigger value=' /opt/arcadia_merged/client/src/pages/BiWorkspace.tsx
|
||||
```
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- MiroFlowControl.tsx existe e exporta componente válido
|
||||
- BiWorkspace.tsx tem 7 TabsTriggers (6 originais + "cientifico")
|
||||
- TypeScript compila sem erros nos novos arquivos
|
||||
- Usuário aprova verificação visual do checkpoint
|
||||
- Tabs existentes do BiWorkspace continuam funcionando
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Após conclusão e aprovação do checkpoint, criar `.planning/phases/03-miroflow-embutido/03-03-SUMMARY.md` com:
|
||||
- Arquivos criados/modificados
|
||||
- Screenshot ou descrição do estado visual da tab "Científico"
|
||||
- Resultado da verificação humana
|
||||
- Qualquer ajuste feito após o checkpoint
|
||||
</output>
|
||||
|
|
@ -0,0 +1,551 @@
|
|||
# Phase 3: MiroFlow Embutido - Research
|
||||
|
||||
**Researched:** 2026-03-25
|
||||
**Domain:** Python agent framework (MiroFlow) integrado ao Node.js/Express + Ollama local + Superset bridge
|
||||
**Confidence:** HIGH (baseado em inspeção direta do código-fonte e serviços ativos)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
O Phase 3 conecta o framework Python MiroFlow (já clonado como submodule em `server/modules/miroflow/`) ao backend Node.js do Arcádia Suite, expondo um endpoint `POST /api/miroflow/analyze` que despacha para um microserviço Python FastAPI. Esse microserviço instancia agentes MiroFlow configurados via YAML, aponta para o Ollama local (já rodando na porta 11434 com `deepseek-r1:14b` instalado), e registra cada execução como nó imutável no KG (tabela `graph_nodes` via `/api/graph/nodes`).
|
||||
|
||||
O padrão de integração Node ↔ Python já está estabelecido no projeto: `server/bi/engine-proxy.ts` e `server/python/bi_analysis_service.py` demonstram o fluxo exato — Express proxy faz `fetch()` para FastAPI local; a resposta é repassada ao cliente. O novo microserviço MiroFlow (`miroflow_service.py`) seguirá o mesmo padrão, rodando na porta 8006. O agente `Researcher` exige `llama3.1:8b`, que **ainda não está instalado no Ollama** (apenas `deepseek-r1:14b`, `llama3.2:3b` e `nomic-embed-text` estão disponíveis).
|
||||
|
||||
No frontend, o componente `MiroFlowControl.tsx` será um painel overlay/tab injetado na página `BiWorkspace.tsx` que já usa o `SupersetDashboard` component. O toggle "Modo Científico" chama `POST /api/miroflow/analyze` e exibe a resposta estruturada ao lado do dashboard Superset.
|
||||
|
||||
**Primary recommendation:** Construir `server/python/miroflow_service.py` como FastAPI standalone (porta 8006), instanciar agentes MiroFlow com YAML inline (sem arquivos externos), usar `UnifiedOpenAIClient` apontando para Ollama via `OLLAMA_BASE_URL/v1`, registrar execuções via `createNode()` do graph service.
|
||||
|
||||
---
|
||||
|
||||
## User Constraints
|
||||
|
||||
*(Seção obrigatória — copiada do STATE.md/ROADMAP.md/PROJECT.md que fazem as vezes de CONTEXT.md)*
|
||||
|
||||
### Locked Decisions
|
||||
- Stack: Node.js + TypeScript backend, React + TypeScript frontend, PostgreSQL, Neo4j KG
|
||||
- IA: Ollama local MÁXIMO 14B — `deepseek-r1:14b` para Statistician e Fiscal Auditor, `llama3.1:8b` para Researcher
|
||||
- **NUNCA usar modelos acima de 14B**: proibido citar ou planejar `deepseek-r1:32b`, `deepseek-r1:70b`, `llama3.1:70b` ou qualquer modelo maior
|
||||
- MiroFlow já clonado em `server/modules/miroflow/` como submodule Python
|
||||
- Branch de deploy: `Servidor`
|
||||
- Superset em produção com RLS — não alterar configurações existentes
|
||||
- Backend-first: API definida antes do frontend
|
||||
- Auditoria imutável em todas as execuções (KG)
|
||||
|
||||
### Claude's Discretion
|
||||
- Porta do microserviço MiroFlow (recomenda-se 8006 pelo padrão existente)
|
||||
- Estrutura interna dos agentes MiroFlow (YAML inline vs arquivos externos)
|
||||
- Forma de integrar o toggle no frontend (overlay, tab, painel lateral)
|
||||
- Como fazer o `llama3.1:8b` ser baixado (wave 0 task vs setup manual)
|
||||
|
||||
### Deferred Ideas (OUT OF SCOPE)
|
||||
- Versionamento Git-like de skills (Phase 2 pendente, não Phase 3)
|
||||
- OpenClaw (Phase 4)
|
||||
- Automation Fabric (Phase 5)
|
||||
- Dev Center Completo (Phase 6)
|
||||
|
||||
---
|
||||
|
||||
## Standard Stack
|
||||
|
||||
### Core
|
||||
| Library | Version | Purpose | Why Standard |
|
||||
|---------|---------|---------|--------------|
|
||||
| MiroFlow | 1.7.0 | Framework de agentes Python | Já no submodule, desenvolvido pela MiromindAI |
|
||||
| FastAPI | >=0.115.0 | Microserviço Python | Padrão já usado em todos os serviços Python do projeto |
|
||||
| uvicorn | >=0.32.0 | ASGI server | Usado em todos os serviços Python |
|
||||
| omegaconf | (via miroflow deps) | Configuração YAML com variáveis | Dependência central do MiroFlow |
|
||||
| hydra-core | >=1.3.2 | Composição de configurações | Dependência central do MiroFlow |
|
||||
| openai | ==1.78.1 | Cliente HTTP para Ollama (OpenAI-compat.) | MiroFlow usa `UnifiedOpenAIClient` via openai SDK |
|
||||
| httpx / aiohttp | >=0.28.1 / >=3.12.15 | HTTP async | Dependências do MiroFlow e FastAPI |
|
||||
|
||||
### Supporting
|
||||
| Library | Version | Purpose | When to Use |
|
||||
|---------|---------|---------|-------------|
|
||||
| python-dotenv | >=1.1.1 | Leitura de .env | Inicialização do microserviço |
|
||||
| pydantic | >=2.x | Validação de request/response | Models FastAPI |
|
||||
| neo4j (driver) | opcional | Neo4j direto | Apenas se precisar grafo real; por ora usa `graph_nodes` PostgreSQL |
|
||||
| psycopg2-binary | >=2.9.x | PostgreSQL direto | Se microserviço precisar escrever no banco |
|
||||
|
||||
### Alternatives Considered
|
||||
| Instead of | Could Use | Tradeoff |
|
||||
|------------|-----------|----------|
|
||||
| Microserviço FastAPI separado | Subprocess Node → Python | subprocess é síncrono e frágil; FastAPI segue padrão estabelecido |
|
||||
| YAML files externos | YAML inline (dict) em Python | Inline elimina dependência de caminhos de arquivo em container |
|
||||
| Ollama direto (porta 11434) | Via LiteLLM gateway (porta 4000) | LiteLLM tem fallback e roteamento; Ollama direto é mais simples para agentes isolados |
|
||||
|
||||
**Installation (para o microserviço):**
|
||||
```bash
|
||||
# Instalar dependências no ambiente Python do servidor
|
||||
pip install fastapi uvicorn omegaconf hydra-core openai python-dotenv pydantic psycopg2-binary
|
||||
# Ou instalar o miroflow inteiro com uv:
|
||||
cd server/modules/miroflow && uv sync
|
||||
```
|
||||
|
||||
**Version verification:** Os pacotes acima são os listados no `pyproject.toml` do MiroFlow v1.7.0, verificado diretamente no arquivo.
|
||||
|
||||
---
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Recommended Project Structure
|
||||
```
|
||||
server/python/
|
||||
├── miroflow_service.py # novo microserviço FastAPI (porta 8006)
|
||||
├── bi_analysis_service.py # existente (referência de padrão)
|
||||
└── ...
|
||||
|
||||
server/miroflow/
|
||||
├── routes.ts # Express router: POST /api/miroflow/analyze
|
||||
└── engine-proxy.ts # fetch() para http://localhost:8006
|
||||
|
||||
client/src/
|
||||
├── pages/BiWorkspace.tsx # EXISTENTE — adicionar tab "Científico"
|
||||
└── components/
|
||||
└── MiroFlowControl.tsx # NOVO — toggle + resultado da análise
|
||||
|
||||
docker/python-entrypoint.sh # adicionar case "miroflow" → porta 8006
|
||||
docker-compose.prod.yml # adicionar serviço miroflow (porta 8006)
|
||||
```
|
||||
|
||||
### Pattern 1: Node → Python Microservice Proxy
|
||||
**What:** Express recebe a request, faz fetch() para FastAPI, retorna o resultado. Sem subprocess, sem child_process.
|
||||
**When to use:** Sempre que Node.js precisar de lógica Python (pandas, ML, agentes).
|
||||
**Example:**
|
||||
```typescript
|
||||
// Source: server/bi/engine-proxy.ts (padrão existente)
|
||||
const MIROFLOW_URL = `http://${process.env.MIROFLOW_HOST || "localhost"}:${process.env.MIROFLOW_PORT || "8006"}`;
|
||||
|
||||
async function proxyToMiroFlow(path: string, body: object): Promise<any> {
|
||||
const response = await fetch(`${MIROFLOW_URL}${path}`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
signal: AbortSignal.timeout(300_000), // 5min — modelos LLM são lentos
|
||||
});
|
||||
if (!response.ok) {
|
||||
const err = await response.json().catch(() => ({ detail: response.statusText }));
|
||||
throw new Error(err.detail || `MiroFlow error: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 2: MiroFlow Agent com Ollama via UnifiedOpenAIClient
|
||||
**What:** MiroFlow usa `UnifiedOpenAIClient` com `base_url` apontando para Ollama (OpenAI-compatible API em `/v1`).
|
||||
**When to use:** Para todos os agentes do Phase 3.
|
||||
**Example:**
|
||||
```python
|
||||
# Source: server/modules/miroflow/miroflow/llm/openai_client.py + config/llm/base.yaml
|
||||
from miroflow.agents.factory import build_agent
|
||||
from miroflow.agents.context import AgentContext
|
||||
|
||||
AGENT_CONFIG_STATISTICIAN = {
|
||||
"type": "IterativeAgentWithToolAndRollback",
|
||||
"max_turns": 10,
|
||||
"llm": {
|
||||
"provider_class": "UnifiedOpenAIClient",
|
||||
"model_name": "deepseek-r1:14b",
|
||||
"api_key": "ollama", # Ollama não valida api_key
|
||||
"base_url": "http://localhost:11434/v1", # Ollama OpenAI-compat
|
||||
"max_tokens": 8192,
|
||||
"temperature": 0.7,
|
||||
"top_p": 1.0, "min_p": 0.0, "top_k": -1,
|
||||
"reasoning_effort": None, "repetition_penalty": 1.0,
|
||||
"max_context_length": -1, "async_client": True,
|
||||
"disable_cache_control": True, "keep_tool_result": -1,
|
||||
"use_tool_calls": False, "oai_tool_thinking": False,
|
||||
},
|
||||
}
|
||||
agent = build_agent(AGENT_CONFIG_STATISTICIAN)
|
||||
ctx = AgentContext(task_description="Analise os seguintes dados SQL: ...")
|
||||
result = await agent.run(ctx)
|
||||
```
|
||||
|
||||
### Pattern 3: Registro Imutável no KG via graph_nodes
|
||||
**What:** Após cada execução MiroFlow, registrar no PostgreSQL via `POST /api/graph/nodes`.
|
||||
**When to use:** Toda execução de agente (success ou error).
|
||||
**Example:**
|
||||
```typescript
|
||||
// Source: server/graph/service.ts createNode() — chamado pelo routes.ts de miroflow
|
||||
await fetch("/api/graph/nodes", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
type: "miroflow_execution",
|
||||
tenantId: req.user?.tenantId,
|
||||
data: {
|
||||
agent: "statistician",
|
||||
model: "deepseek-r1:14b",
|
||||
input: analysisRequest,
|
||||
output: analysisResult,
|
||||
auditHash: sha256(JSON.stringify({ executionId, input, output })),
|
||||
executedAt: new Date().toISOString(),
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
### Pattern 4: MiroFlowControl.tsx como tab em BiWorkspace
|
||||
**What:** BiWorkspace.tsx já tem estrutura de tabs (Tabs/TabsContent de shadcn/ui). Adicionar tab "Científico" que renderiza `MiroFlowControl`.
|
||||
**When to use:** Ponto de entrada do toggle "Modo Científico" no contexto do BI.
|
||||
**Example:**
|
||||
```tsx
|
||||
// Source: client/src/pages/BiWorkspace.tsx (padrão de tabs existente)
|
||||
import { MiroFlowControl } from "@/components/MiroFlowControl";
|
||||
|
||||
// Dentro do <TabsList>:
|
||||
<TabsTrigger value="cientifico"><Brain /> Científico</TabsTrigger>
|
||||
|
||||
// Dentro das <TabsContent>:
|
||||
<TabsContent value="cientifico">
|
||||
<MiroFlowControl currentDashboardData={currentData} />
|
||||
</TabsContent>
|
||||
```
|
||||
|
||||
### Anti-Patterns to Avoid
|
||||
- **subprocess.run() no Node**: Não usar `child_process.spawn()` para chamar Python — use o microserviço FastAPI (padrão estabelecido)
|
||||
- **Carregar miroflow no container Node**: O Dockerfile principal é Node.js puro — Python deve rodar em container separado (Dockerfile.python)
|
||||
- **Modelos > 14B**: Nunca usar `deepseek-r1:32b`, `deepseek-r1:70b`, `llama3.1:70b` ou qualquer modelo acima de 14B parâmetros
|
||||
- **Modificar configurações do Superset**: O proxy `/superset/*` e as rotas `/api/superset/*` existentes não devem ser alterados
|
||||
- **Hardcoded OLLAMA_BASE_URL**: Sempre ler de env var `OLLAMA_BASE_URL` (default `http://localhost:11434`)
|
||||
|
||||
---
|
||||
|
||||
## Don't Hand-Roll
|
||||
|
||||
| Problem | Don't Build | Use Instead | Why |
|
||||
|---------|-------------|-------------|-----|
|
||||
| Orquestração de agentes LLM | Loop manual de chamadas LLM | MiroFlow `IterativeAgentWithToolAndRollback` | Rollback automático, retry, tool calling já implementado |
|
||||
| Proxy Node → Python | Child process / spawn | `fetch()` para FastAPI (padrão `engine-proxy.ts`) | Stateless, async, sem gestão de processos |
|
||||
| Configuração de LLM | Classe própria de cliente | `UnifiedOpenAIClient` do MiroFlow | Já tem retry, context limit handling, tool protocol |
|
||||
| Embedding da análise no Superset | iframe manual com JS | `SupersetDashboard.tsx` já existente | Guest token, SDK do Superset, error handling prontos |
|
||||
| Logs de execução imutáveis | Nova tabela no banco | `graph_nodes` + `createNode()` via `server/graph/service.ts` | Infraestrutura de KG já existente com embeddings |
|
||||
| Validação de request/response | Manual | Pydantic models + FastAPI auto-validation | Type safety e OpenAPI spec grátis |
|
||||
|
||||
**Key insight:** O projeto já tem todos os primitivos necessários. Phase 3 é essencialmente "conectar os pontos" — o agente Python já existe, o gateway LLM (LiteLLM/Ollama) já está configurado, o KG já tem tabelas, o padrão de proxy Node→Python já está implementado.
|
||||
|
||||
---
|
||||
|
||||
## Runtime State Inventory
|
||||
|
||||
> Fase é greenfield (novo microserviço e novas rotas). Não há renomeações ou migrações de dados existentes.
|
||||
|
||||
| Category | Items Found | Action Required |
|
||||
|----------|-------------|------------------|
|
||||
| Stored data | `graph_nodes` no PostgreSQL — usado pelo KG existente | Nenhum — novas inserções com `type: "miroflow_execution"` |
|
||||
| Live service config | Ollama rodando na porta 11434 com `deepseek-r1:14b` instalado | `llama3.1:8b` NÃO instalado — precisa de `ollama pull llama3.1:8b` |
|
||||
| OS-registered state | Nenhum Task Scheduler / cron para MiroFlow | Nenhuma ação necessária |
|
||||
| Secrets/env vars | `OLLAMA_BASE_URL=http://ollama:11434` já no `.env`; `LITELLM_API_KEY` existente | Adicionar `MIROFLOW_PORT=8006` no `.env` e docker-compose |
|
||||
| Build artifacts | `server/modules/miroflow/` tem `uv.lock` mas sem `.venv` instalado | Wave 0: instalar dependências via `uv sync` ou `pip install` no Dockerfile.python |
|
||||
|
||||
**Modelo faltando:** `llama3.1:8b` não está instalado no Ollama (`False` confirmado por `curl http://localhost:11434/api/tags`). Instalado: `deepseek-r1:14b` (8.4GB), `arcadia-agent:latest` (2GB), `llama3.2:3b` (2GB), `nomic-embed-text` (274MB). O plano deve incluir `ollama pull llama3.1:8b` como task de Wave 0 ou pré-requisito.
|
||||
|
||||
---
|
||||
|
||||
## Environment Availability
|
||||
|
||||
| Dependency | Required By | Available | Version | Fallback |
|
||||
|------------|------------|-----------|---------|----------|
|
||||
| Ollama API `:11434` | Todos os agentes MiroFlow | ✓ | 0.18.2 | — |
|
||||
| `deepseek-r1:14b` | Statistician, Fiscal Auditor | ✓ | 8.4GB | — |
|
||||
| `llama3.1:8b` | Researcher | ✗ | — | `llama3.2:3b` (degraded) ou `deepseek-r1:14b` |
|
||||
| Python 3.12 | Microserviço FastAPI | ✓ | 3.12.3 | — |
|
||||
| `uv` | Instalar deps MiroFlow | ✓ | 0.9.30 | `pip3` |
|
||||
| FastAPI/uvicorn | Microserviço | ✓ | fastapi 0.128.8 | — |
|
||||
| `omegaconf` (sistema) | MiroFlow direto | ✗ | — | Instalar via `uv sync` no `server/modules/miroflow/` |
|
||||
| PostgreSQL `:5432` | KG graph_nodes | ✓ | 16 | — |
|
||||
| Neo4j `:7687` | KG real (opcional) | Requer profile `[kg]` | — | `graph_nodes` PostgreSQL (já usado) |
|
||||
|
||||
**Missing dependencies with no fallback:**
|
||||
- `llama3.1:8b` para o agente Researcher — deve ser baixado em Wave 0 (`ollama pull llama3.1:8b`)
|
||||
|
||||
**Missing dependencies with fallback:**
|
||||
- `omegaconf` no sistema: instalar com `pip3 install omegaconf hydra-core` ou rodar MiroFlow dentro de venv com `uv sync`
|
||||
- `llama3.1:8b` temporariamente: usar `deepseek-r1:14b` como fallback até o download completar
|
||||
|
||||
---
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### Pitfall 1: Ollama não expõe `/v1` sem flag explícita em versões antigas
|
||||
**What goes wrong:** `UnifiedOpenAIClient` do MiroFlow usa `base_url=http://localhost:11434/v1` (OpenAI-compatible endpoint). Versões antigas do Ollama (<0.1.24) não tinham esse endpoint.
|
||||
**Why it happens:** Ollama 0.18.2 (confirmado no servidor) já suporta `/v1/chat/completions`. Mas o endpoint retorna erro se o modelo não estiver carregado.
|
||||
**How to avoid:** Verificar `GET http://localhost:11434/api/tags` antes de iniciar o agente. Pré-carregar o modelo com `ollama run deepseek-r1:14b` ou `ollama pull`.
|
||||
**Warning signs:** `404 Not Found` na URL `/v1/chat/completions`, ou `model not found` no corpo da resposta.
|
||||
|
||||
### Pitfall 2: MiroFlow requer dependências Python não instaladas no container Node
|
||||
**What goes wrong:** O `Dockerfile` principal é Node.js Alpine — não tem Python. Se tentar importar `miroflow` no contexto Node (via python-bridge ou similar), vai falhar.
|
||||
**Why it happens:** A tentativa de `from omegaconf import ...` falha com `ModuleNotFoundError` porque o sistema Python não tem as deps do MiroFlow.
|
||||
**How to avoid:** Manter a separação clara: MiroFlow roda SOMENTE em `Dockerfile.python` como microserviço FastAPI. Node nunca importa Python diretamente.
|
||||
**Warning signs:** Erro de `ModuleNotFoundError: No module named 'omegaconf'` nos logs do app principal.
|
||||
|
||||
### Pitfall 3: Timeout do fetch() insuficiente para modelos grandes
|
||||
**What goes wrong:** `deepseek-r1:14b` com raciocínio pode levar 60-120 segundos para responder. O fetch padrão tem timeout de 30s nos outros proxies.
|
||||
**Why it happens:** O `bi/engine-proxy.ts` usa `BI_ENGINE_TIMEOUT = 30000`. Para LLMs de 14B, isso é insuficiente.
|
||||
**How to avoid:** Usar `AbortSignal.timeout(300_000)` (5 minutos) no proxy MiroFlow. Implementar streaming ou polling se necessário.
|
||||
**Warning signs:** `AbortError: The operation was aborted` nos logs do Node.
|
||||
|
||||
### Pitfall 4: Neo4j não está ativo por padrão — usar graph_nodes do PostgreSQL
|
||||
**What goes wrong:** O `docker-compose.yml` tem Neo4j no profile `[kg]` — não está ativo em deploy padrão.
|
||||
**Why it happens:** Neo4j é opcional; o KG do Arcádia usa `graph_nodes` PostgreSQL por padrão.
|
||||
**How to avoid:** Para imutabilidade, usar `POST /api/graph/nodes` (PostgreSQL), não o driver `neo4j` Python direto. O `server/graph/service.ts` já implementa isso com hashing.
|
||||
**Warning signs:** `Connection refused: 7687` se tentar conectar ao Neo4j sem o profile ativo.
|
||||
|
||||
### Pitfall 5: MiroFlow não tem `llama3.1:8b` instalado
|
||||
**What goes wrong:** O agente Researcher usa `llama3.1:8b` mas esse modelo não está no Ollama (`False` confirmado por inspeção direta).
|
||||
**Why it happens:** Apenas `deepseek-r1:14b`, `arcadia-agent:latest`, `llama3.2:3b` e `nomic-embed-text` foram instalados.
|
||||
**How to avoid:** Incluir `ollama pull llama3.1:8b` como step explícito na Wave 0 do plano.
|
||||
**Warning signs:** Agente Researcher falha com `model 'llama3.1:8b' not found`.
|
||||
|
||||
### Pitfall 6: BiWorkspace.tsx é uma página grande — cuidado ao modificar
|
||||
**What goes wrong:** `BiWorkspace.tsx` tem muitas funcionalidades (datasets, charts, dashboards, backup, BI engine). Modificação descuidada pode quebrar features existentes.
|
||||
**Why it happens:** O arquivo tem ~1000+ linhas com múltiplas tabs e estados.
|
||||
**How to avoid:** Adicionar MiroFlowControl como componente SEPARADO importado, não modificar lógica existente. Adicionar apenas uma nova `TabsTrigger` e `TabsContent`.
|
||||
**Warning signs:** Erros de TypeScript em outros tabs depois da edição.
|
||||
|
||||
---
|
||||
|
||||
## Code Examples
|
||||
|
||||
Verified patterns from official sources:
|
||||
|
||||
### MiroFlow Agent com Ollama (config inline Python)
|
||||
```python
|
||||
# Source: server/modules/miroflow/miroflow/agents/base.py + config/llm/base.yaml
|
||||
import sys
|
||||
sys.path.insert(0, "/app/server/modules/miroflow")
|
||||
|
||||
import asyncio
|
||||
from miroflow.agents.factory import build_agent
|
||||
from miroflow.agents.context import AgentContext
|
||||
|
||||
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://localhost:11434")
|
||||
|
||||
def make_agent_cfg(model: str) -> dict:
|
||||
return {
|
||||
"type": "IterativeAgentWithToolAndRollback",
|
||||
"name": f"arcadia_{model.replace(':', '_')}",
|
||||
"max_turns": 10,
|
||||
"llm": {
|
||||
"provider_class": "UnifiedOpenAIClient",
|
||||
"model_name": model,
|
||||
"api_key": "ollama",
|
||||
"base_url": f"{OLLAMA_BASE_URL}/v1",
|
||||
"max_tokens": 8192,
|
||||
"temperature": 0.7,
|
||||
"top_p": 1.0, "min_p": 0.0, "top_k": -1,
|
||||
"reasoning_effort": None, "repetition_penalty": 1.0,
|
||||
"max_context_length": -1, "async_client": True,
|
||||
"disable_cache_control": True, "keep_tool_result": -1,
|
||||
"use_tool_calls": False, "oai_tool_thinking": False,
|
||||
},
|
||||
}
|
||||
|
||||
async def run_agent(agent_type: str, task: str) -> str:
|
||||
model = "deepseek-r1:14b" if agent_type in ("statistician", "fiscal_auditor") else "llama3.1:8b"
|
||||
cfg = make_agent_cfg(model)
|
||||
agent = build_agent(cfg)
|
||||
ctx = AgentContext(task_description=task)
|
||||
result = await agent.run(ctx)
|
||||
return result.get("summary", str(result))
|
||||
```
|
||||
|
||||
### FastAPI endpoint pattern (seguindo bi_analysis_service.py)
|
||||
```python
|
||||
# Source: server/python/bi_analysis_service.py (padrão existente)
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI(title="Arcádia MiroFlow Service", version="1.0.0")
|
||||
|
||||
class AnalyzeRequest(BaseModel):
|
||||
agent: str # "statistician" | "fiscal_auditor" | "researcher"
|
||||
task: str
|
||||
context: dict = {}
|
||||
tenant_id: int | None = None
|
||||
|
||||
class AnalyzeResponse(BaseModel):
|
||||
agent: str
|
||||
model: str
|
||||
result: str
|
||||
execution_id: str
|
||||
duration_ms: int
|
||||
|
||||
@app.post("/analyze", response_model=AnalyzeResponse)
|
||||
async def analyze(req: AnalyzeRequest):
|
||||
...
|
||||
```
|
||||
|
||||
### Express proxy para MiroFlow (seguindo engine-proxy.ts)
|
||||
```typescript
|
||||
// Source: server/bi/engine-proxy.ts (padrão existente)
|
||||
const MIROFLOW_URL = `http://${process.env.MIROFLOW_HOST || "localhost"}:${
|
||||
process.env.MIROFLOW_PORT || "8006"
|
||||
}`;
|
||||
|
||||
export function registerMiroFlowRoutes(app: Express): void {
|
||||
app.post("/api/miroflow/analyze", async (req: Request, res: Response) => {
|
||||
if (!req.isAuthenticated()) return res.status(401).json({ error: "Não autenticado" });
|
||||
try {
|
||||
const response = await fetch(`${MIROFLOW_URL}/analyze`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ ...req.body, tenant_id: (req.user as any)?.tenantId }),
|
||||
signal: AbortSignal.timeout(300_000),
|
||||
});
|
||||
const data = await response.json();
|
||||
// Registrar no KG
|
||||
await registerExecutionInKG(req, data);
|
||||
res.json(data);
|
||||
} catch (err: any) {
|
||||
res.status(502).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/api/miroflow/health", async (_req, res) => {
|
||||
try {
|
||||
const r = await fetch(`${MIROFLOW_URL}/health`, { signal: AbortSignal.timeout(5000) });
|
||||
res.json({ online: r.ok, url: MIROFLOW_URL });
|
||||
} catch {
|
||||
res.json({ online: false, url: MIROFLOW_URL });
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Registro no KG (graph_nodes)
|
||||
```typescript
|
||||
// Source: server/graph/service.ts createNode()
|
||||
import { createNode } from "../graph/service";
|
||||
import crypto from "crypto";
|
||||
|
||||
async function registerExecutionInKG(req: any, execData: any) {
|
||||
const auditHash = crypto
|
||||
.createHash("sha256")
|
||||
.update(JSON.stringify(execData))
|
||||
.digest("hex");
|
||||
await createNode({
|
||||
type: "miroflow_execution",
|
||||
tenantId: req.user?.tenantId,
|
||||
data: { ...execData, auditHash, immutable: true },
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## State of the Art
|
||||
|
||||
| Old Approach | Current Approach | When Changed | Impact |
|
||||
|--------------|------------------|--------------|--------|
|
||||
| Llamaindex/LangChain para agentes | MiroFlow (framework próprio MiromindAI) | 2025-08 | Performance-first, benchmarks públicos, já no submodule |
|
||||
| OpenAI API direta | Ollama local via LiteLLM gateway | 2025 (Arcádia) | Soberania total dos dados |
|
||||
| Neo4j obrigatório para KG | graph_nodes PostgreSQL + Neo4j opcional | Phase 1 | Sem dependência de serviço extra em produção básica |
|
||||
| Hydra config files externos | Config dict inline (Python) | MiroFlow 1.x | Sem dependência de paths, mais fácil em containers |
|
||||
|
||||
**Deprecated/outdated:**
|
||||
- MiroFlow via CLI (`python run_single_task.py`): adequado para benchmark, mas não para microserviço de produção. Usar `web_app/main.py` como referência OU criar FastAPI própria.
|
||||
- Modelos > 14B: explicitamente proibidos na arquitetura do projeto.
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **`llama3.1:8b` vs `llama3.2:3b` para Researcher**
|
||||
- What we know: `llama3.1:8b` não está instalado; `llama3.2:3b` está (2GB, já disponível)
|
||||
- What's unclear: Se João quer baixar `llama3.1:8b` (4.7GB a mais) ou aceitar `llama3.2:3b` como substituto para o Researcher
|
||||
- Recommendation: Plano deve incluir `ollama pull llama3.1:8b` como Wave 0, com fallback para `llama3.2:3b` se bandwidth for problema
|
||||
|
||||
2. **MiroFlow via web_app existente vs FastAPI própria**
|
||||
- What we know: `server/modules/miroflow/web_app/main.py` é uma FastAPI completa com session manager e task executor; `server/python/bi_analysis_service.py` é um FastAPI simples sem estado
|
||||
- What's unclear: Usar o `web_app` completo do MiroFlow (mais funcionalidades) ou escrever `miroflow_service.py` minimal seguindo o padrão de outros serviços
|
||||
- Recommendation: Escrever `miroflow_service.py` minimal — mais simples, sem session management, sem frontend próprio do MiroFlow
|
||||
|
||||
3. **Registro de execuções: graph_nodes vs skill_executions**
|
||||
- What we know: `skill_executions` é específico para Skills Arcádia; `graph_nodes` é KG genérico
|
||||
- What's unclear: O requirement diz "KG" — se é necessário usar o Neo4j real ou PostgreSQL `graph_nodes` é suficiente
|
||||
- Recommendation: Usar `graph_nodes` PostgreSQL com `type: "miroflow_execution"` — atende o requisito de imutabilidade sem depender do Neo4j (profile opcional)
|
||||
|
||||
4. **Posicionamento do MiroFlowControl na UI**
|
||||
- What we know: `BiWorkspace.tsx` já tem sistema de tabs (LayoutDashboard, Database, BarChart3...); `Scientist.tsx` já existe como página standalone de análise científica
|
||||
- What's unclear: Se o toggle deve ficar dentro do BiWorkspace (access contextual) ou se deve ser um botão flutuante ou se Scientist.tsx deve ser integrado ao BI
|
||||
- Recommendation: Tab "Científico" dentro de `BiWorkspace.tsx` — mais coerente com o objetivo "integrado ao Superset"
|
||||
|
||||
---
|
||||
|
||||
## Validation Architecture
|
||||
|
||||
> `workflow.nyquist_validation` não encontrado em `.planning/config.json` (arquivo não existe). Tratado como habilitado.
|
||||
|
||||
### Test Framework
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Framework | pytest (Python) + vitest/jest implícito (TS) |
|
||||
| Config file | Nenhum — Wave 0 deve criar `pytest.ini` para o microserviço |
|
||||
| Quick run command | `pytest server/python/test_miroflow_service.py -x` |
|
||||
| Full suite command | `pytest server/python/ -v` |
|
||||
|
||||
### Phase Requirements → Test Map
|
||||
| Req ID | Behavior | Test Type | Automated Command | File Exists? |
|
||||
|--------|----------|-----------|-------------------|-------------|
|
||||
| REQ-3.1 | MiroFlow configurado para Ollama local ≤14B | integration | `pytest tests/test_miroflow_ollama.py -x` | ❌ Wave 0 |
|
||||
| REQ-3.2 | Agente Statistician analisa SQL com deepseek-r1:14b | integration | `pytest tests/test_miroflow_service.py::test_statistician -x` | ❌ Wave 0 |
|
||||
| REQ-3.3 | Agente Fiscal Auditor valida NFe/SPED com deepseek-r1:14b | integration | `pytest tests/test_miroflow_service.py::test_fiscal_auditor -x` | ❌ Wave 0 |
|
||||
| REQ-3.4 | Agente Researcher consulta KG com llama3.1:8b | integration | `pytest tests/test_miroflow_service.py::test_researcher -x` | ❌ Wave 0 |
|
||||
| REQ-3.5 | `POST /api/miroflow/analyze` retorna análise estruturada | integration | `curl -X POST localhost:5000/api/miroflow/analyze ...` | ❌ Wave 0 |
|
||||
| REQ-3.6 | MiroFlowControl.tsx toggle aparece no Superset/BiWorkspace | manual | Inspeção visual no browser | N/A |
|
||||
| REQ-3.7 | Execuções registradas com imutabilidade no KG | unit | `pytest tests/test_miroflow_kg.py -x` | ❌ Wave 0 |
|
||||
|
||||
### Sampling Rate
|
||||
- **Per task commit:** `pytest server/python/test_miroflow_service.py -x`
|
||||
- **Per wave merge:** `pytest server/python/ -v`
|
||||
- **Phase gate:** Health check `/api/miroflow/health` retorna `{"online": true}` + todos os 3 agentes respondem com modelo correto
|
||||
|
||||
### Wave 0 Gaps
|
||||
- [ ] `server/python/test_miroflow_service.py` — cobre REQ-3.1, 3.2, 3.3, 3.4, 3.5, 3.7
|
||||
- [ ] `server/python/miroflow_service.py` — o microserviço principal
|
||||
- [ ] `server/miroflow/routes.ts` + `engine-proxy.ts` — rotas Express
|
||||
- [ ] `client/src/components/MiroFlowControl.tsx` — componente React
|
||||
- [ ] `ollama pull llama3.1:8b` — modelo necessário para Researcher (Wave 0 setup)
|
||||
- [ ] Instalar dependências MiroFlow: `pip install omegaconf hydra-core openai` (ou `uv sync` no diretório do submodule)
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
|
||||
### Primary (HIGH confidence)
|
||||
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/pyproject.toml` — versão 1.7.0, dependências confirmadas
|
||||
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/miroflow/llm/openai_client.py` — `UnifiedOpenAIClient`, `base_url` configurável
|
||||
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/miroflow/agents/base.py` — `BaseAgent`, `AgentContext`, `build_agent()`
|
||||
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/config/llm/base.yaml` — campos obrigatórios do LLM config
|
||||
- Inspeção direta: `/opt/arcadia_merged/server/bi/engine-proxy.ts` — padrão estabelecido de proxy Node → Python
|
||||
- Inspeção direta: `/opt/arcadia_merged/server/python/bi_analysis_service.py` — padrão do microserviço FastAPI
|
||||
- Inspeção direta: `/opt/arcadia_merged/docker/litellm-config.yaml` — `deepseek-r1:14b` já configurado como `arcadia-default`
|
||||
- Inspeção direta: `/opt/arcadia_merged/docker-compose.prod.yml` — `OLLAMA_BASE_URL` configurado, serviço `ollama` com profile `[ai]`
|
||||
- Runtime check: `curl http://localhost:11434/api/tags` — modelos instalados confirmados
|
||||
- Runtime check: `curl http://localhost:11434/api/version` — Ollama 0.18.2 com `/v1` support
|
||||
|
||||
### Secondary (MEDIUM confidence)
|
||||
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/miroflow/agents/iterative_agent_with_rollback.py` — tipo `IterativeAgentWithToolAndRollback` confirmado
|
||||
- Inspeção direta: `/opt/arcadia_merged/server/modules/miroflow/web_app/main.py` — FastAPI web_app existente no submodule (alternativa não usada)
|
||||
- Inspeção direta: `/opt/arcadia_merged/server/graph/service.ts` — `createNode()`, `graph_nodes` tabela
|
||||
|
||||
### Tertiary (LOW confidence)
|
||||
- Inferência sobre tempo de resposta do `deepseek-r1:14b` (60-120s) baseada em conhecimento geral de modelos 14B — não medido diretamente no servidor
|
||||
|
||||
---
|
||||
|
||||
## Metadata
|
||||
|
||||
**Confidence breakdown:**
|
||||
- Standard stack: HIGH — verificado diretamente nos arquivos do submodule e nas dependências instaladas
|
||||
- Architecture: HIGH — padrões copiados de código existente funcionando em produção
|
||||
- Pitfalls: HIGH (3-5) e MEDIUM (1-2) — baseados em inspeção direta do runtime e código
|
||||
- Ambiente: HIGH — testado com curl direto ao Ollama e listagem de modelos
|
||||
|
||||
**Research date:** 2026-03-25
|
||||
**Valid until:** 2026-04-25 (30 dias; stack estável, Ollama versão fixada)
|
||||
|
||||
---
|
||||
|
||||
## RESEARCH COMPLETE
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
# Phase 6 Context — Dev Center Completo
|
||||
|
||||
> Generated in --auto mode on 2026-03-26. All decisions auto-selected with recommended defaults.
|
||||
|
||||
## Phase Goal
|
||||
|
||||
Fábrica completa de agentes integrada ao DevCenter existente: Design → Assemble → Deploy, com galeria de agentes por tenant.
|
||||
|
||||
## Prior Context Applied
|
||||
|
||||
- **Stack Node.js/TypeScript + React:** sem novos microserviços
|
||||
- **Backend-first:** tabela + API antes do frontend
|
||||
- **Blackboard já existe:** `server/blackboard/` com ArchitectAgent, GeneratorAgent, ValidatorAgent, ExecutorAgent — reusar sem modificar
|
||||
- **DevCenter.tsx já existe em `/dev-center`:** adicionar tabs, não criar nova rota
|
||||
- **Schema modular:** nova tabela pode ir em `shared/schema.ts` (é a fase certa — tabela de agentes faz parte do core do sistema)
|
||||
- **Auditoria imutável:** execuções e artefatos já logados via `blackboard_artifacts`
|
||||
|
||||
---
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Onde vive a nova UI?
|
||||
|
||||
**[auto] 4 novos tabs no DevCenter.tsx existente — sem nova rota**
|
||||
|
||||
- Tabs adicionados ao `<TabsList>` existente em `/dev-center`
|
||||
- Tabs novos: `design`, `assemble`, `deploy`, `galeria`
|
||||
- Tabs existentes (Desenvolver, Status, Analisar Repos, Ferramentas, Sistema, Preview, Histórico) permanecem intocados
|
||||
- Nenhuma nova rota no App.tsx, nenhum novo import lazy
|
||||
|
||||
### 2. DesignStudio — modos do editor
|
||||
|
||||
**[auto] 3 modos: Markdown Spec, Code Editor, Visual Flow**
|
||||
|
||||
- **Markdown Spec** (padrão): textarea com Monaco em modo `markdown` — usuário descreve o agente em linguagem natural estruturada
|
||||
- **Code Editor**: Monaco em modo `typescript` — para specs técnicas detalhadas ou código direto
|
||||
- **Visual Flow**: lista de nós editáveis (JSON simples) — nome/tipo/conexões — NÃO um editor gráfico completo
|
||||
- UML = descrito via texto em Markdown Spec (sem biblioteca de diagramas)
|
||||
- Seletor de modo: 3 botões inline no topo do tab Design
|
||||
|
||||
### 3. Tabela de definições de agentes
|
||||
|
||||
**[auto] Nova tabela `arcadia_agent_defs` em `shared/schema.ts`**
|
||||
|
||||
Campos:
|
||||
- `id` (serial PK)
|
||||
- `tenantId` (varchar, nullable)
|
||||
- `userId` (varchar — criador)
|
||||
- `name` (varchar — nome do agente)
|
||||
- `description` (text)
|
||||
- `spec` (jsonb — conteúdo do DesignStudio: `{ mode, content }`)
|
||||
- `status` (varchar — `draft | assembling | ready | deployed`)
|
||||
- `version` (integer, default 1)
|
||||
- `lastTaskId` (integer — FK para blackboard_tasks, nullable)
|
||||
- `createdAt`, `updatedAt` (timestamp)
|
||||
|
||||
API: `/api/agent-defs` — CRUD padrão (GET list, POST create, PATCH update, DELETE)
|
||||
|
||||
### 4. AssembleLine — fluxo de montagem
|
||||
|
||||
**[auto] DesignStudio spec → POST /api/blackboard/task → GeneratorAgent → artefato linkado**
|
||||
|
||||
- Tab "Assemble" lista `arcadia_agent_defs` com status `draft`
|
||||
- Botão "Montar" por agente: POST `/api/blackboard/task` com spec como contexto
|
||||
- `title`: `"Montar agente: ${name}"`
|
||||
- `description`: conteúdo do spec
|
||||
- `context`: `{ agentDefId, spec, source: "agent-factory" }`
|
||||
- Atualiza `arcadia_agent_defs.status` → `assembling`, salva `lastTaskId`
|
||||
- Progresso: polling de `/api/blackboard/task/:id` a cada 3s
|
||||
- Ao completar: status → `ready`, artefato gerado visível no painel de detalhes
|
||||
- Reusar completamente os agentes Blackboard existentes — zero modificação
|
||||
|
||||
### 5. OrchestrateCenter — deploy e monitoramento
|
||||
|
||||
**[auto] Lista de agent_defs com ações de deploy + viewer de artefatos**
|
||||
|
||||
- Tab "Deploy": lista `arcadia_agent_defs` em status `ready` ou `deployed`
|
||||
- Ação "Deploy": PATCH status → `deployed`, incrementa `version`
|
||||
- Ação "Re-montar": volta para `draft`, decrementa version (permite iteração)
|
||||
- Monitoramento: expandir linha → mostra último `blackboard_artifact` do `lastTaskId`
|
||||
- Versionamento: campo `version` incrementado a cada novo deploy
|
||||
- Sem infra externa — deploy = mudança de status no banco
|
||||
|
||||
### 6. Galeria de agentes
|
||||
|
||||
**[auto] Grid de cards por tenant — tab "Galeria"**
|
||||
|
||||
- Tab "Galeria": grid de cards com `arcadia_agent_defs` onde `status = "deployed"`
|
||||
- Cada card: nome, descrição, badge de status, número de versão, criador
|
||||
- Ação "Executar": POST `/api/blackboard/task` com spec do agente → cria nova task de execução
|
||||
- Ação "Fork": duplica o agente def como novo draft (copia spec, incrementa nome)
|
||||
- Filtro por status (todos / somente deployed) e busca por nome
|
||||
- Empty state: "Nenhum agente implantado — crie um na aba Design"
|
||||
|
||||
---
|
||||
|
||||
## Reusable Assets Identified
|
||||
|
||||
- `server/blackboard/routes.ts` — `POST /api/blackboard/task`, `GET /api/blackboard/task/:id`, `GET /api/blackboard/tasks` — reusar sem modificar
|
||||
- `server/blackboard/service.ts` — `createMainTask()`, `getTaskWithDetails()` — reusar
|
||||
- `shared/schema.ts` — `blackboardTasks`, `blackboardArtifacts` — leitura e join
|
||||
- `client/src/pages/DevCenter.tsx` — estrutura de tabs existente — adicionar sem remover
|
||||
- `client/src/pages/AutomationCenter.tsx` — referência de UX: lista com filtros + badges
|
||||
- `client/src/pages/Skills.tsx` — referência de galeria com cards
|
||||
- Monaco Editor já instalado (usado no DevCenter tab "Desenvolver")
|
||||
|
||||
---
|
||||
|
||||
## Scope Boundary
|
||||
|
||||
**Incluído nesta fase:**
|
||||
- Tabela `arcadia_agent_defs` + migration + API CRUD `/api/agent-defs`
|
||||
- Tab "Design" (DesignStudio: 3 modos + Monaco)
|
||||
- Tab "Assemble" (lista de defs + botão Montar + progresso polling)
|
||||
- Tab "Deploy" (OrchestrateCenter: lista ready/deployed + ações deploy/re-montar + viewer)
|
||||
- Tab "Galeria" (grid por tenant + run + fork)
|
||||
- Backend endpoint `POST /api/agent-defs/:id/deploy` e `POST /api/agent-defs/:id/run`
|
||||
|
||||
**Fora de escopo (phases futuras):**
|
||||
- Editor gráfico visual com React Flow ou similar
|
||||
- Execução real do código gerado em sandbox (Phase 7)
|
||||
- Integração com CI/CD ou containers reais
|
||||
- Sharing de agentes entre tenants
|
||||
|
||||
---
|
||||
|
||||
## Plan Breakdown Suggested
|
||||
|
||||
- **06-01:** `shared/schema.ts` (tabela `arcadia_agent_defs`) + `server/agent-defs/routes.ts` (CRUD + `/deploy` + `/run`) + registro em `server/routes.ts`
|
||||
- **06-02:** Tab "Design" (DesignStudio 3 modos) + Tab "Assemble" (lista + polling de progresso) no DevCenter.tsx
|
||||
- **06-03:** Tab "Deploy" (OrchestrateCenter) + Tab "Galeria" (grid + run/fork) no DevCenter.tsx
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
# GSD Session Report
|
||||
|
||||
**Generated:** 2026-03-25 09:50 BRT
|
||||
**Project:** Arcádia Suite — `arcadiasuite` (branch `Servidor`)
|
||||
**Milestone:** Arcádia Agentic Suite — 6 Fases (~12 semanas)
|
||||
|
||||
---
|
||||
|
||||
## Session Summary
|
||||
|
||||
**Período:** 2026-03-24 11:30 → 2026-03-25 09:50 (sessões consecutivas)
|
||||
**Fase atual:** Fase 2 — Skills Engine
|
||||
**Commits nesta sessão:** 7
|
||||
**Arquivos alterados:** 29 (+1.875 / -106 linhas)
|
||||
|
||||
---
|
||||
|
||||
## Work Performed
|
||||
|
||||
### Fase 1 — Fundação (CONCLUÍDA)
|
||||
|
||||
- `1c6c386` — SkillEngine POO + ReferenceParser + ReferenceResolver + schema (`arcadia_skills`, `skill_executions`)
|
||||
- `66d8dfb` — MiroFlow e OpenClaw adicionados como git submodules
|
||||
- `c9a0563` — Neo4j no Docker Compose (profile `kg`), Skills.tsx UI, melhorias Manus/chat/Agent
|
||||
|
||||
### Fase 2 — Skills Engine (EM ANDAMENTO)
|
||||
|
||||
- `3c61acf` — XOS: forms de cadastro restaurados (Novo Contato, Negócio, Atividade)
|
||||
- `9bfc888` — Migration: suporte a upload RAR, SQL, SQL.GZ
|
||||
- `194a8dc` — Rota `/skills` no App.tsx + autocomplete de referências `/` no Monaco
|
||||
- `4057821` — Skill Marketplace (Biblioteca): `GET /api/skills/marketplace` + `POST /import` + UI grid
|
||||
|
||||
### Key Outcomes
|
||||
|
||||
- **SkillEngine** com herança POO (`extends`), composição via referências `/`, audit hash SHA-256
|
||||
- **API REST** completa: CRUD + execute + histórico em `/api/skills`
|
||||
- **Editor Monaco** com 3 tabs (Geral / Body / Params) + autocomplete de 9 prefixos de referências
|
||||
- **Skill Marketplace** (Biblioteca): discovery de skills `system`, importação para `tenant` com clonagem + `extends` para origem
|
||||
- **Neo4j** disponível via `docker compose --profile kg up`
|
||||
- **Submodules** MiroFlow e OpenClaw presentes em `server/modules/`
|
||||
|
||||
---
|
||||
|
||||
## Files Changed (resumo por área)
|
||||
|
||||
| Área | Arquivos | Linhas |
|
||||
|------|----------|--------|
|
||||
| Skills (server) | `engine.ts`, `routes.ts`, `reference-parser.ts` | +400 |
|
||||
| Skills (client) | `Skills.tsx` | +570 |
|
||||
| Schema | `shared/schema.ts` | +96 |
|
||||
| Infra | `docker-compose.yml`, `docker/litellm-config.yaml` | +35 |
|
||||
| Manus/Chat | `service.ts`, `routes.ts`, `prompt.ts`, `routes.ts` | +350 |
|
||||
| XOS | `XosCentral.tsx` | +200 |
|
||||
| App | `App.tsx` | +2 |
|
||||
| Outros | Agent, ProcessCompass, bi, compass, ide, etc. | +222 |
|
||||
|
||||
---
|
||||
|
||||
## Blockers & Open Items
|
||||
|
||||
**Fase 2 — pendente:**
|
||||
- [ ] Sistema de versionamento de skills (Git-like)
|
||||
|
||||
**Pendências gerais (produção):**
|
||||
- [ ] Commitar RLS Superset → main
|
||||
- [ ] SOE.tsx stub → importar versão completa do Replit
|
||||
- [ ] Seed de regras padrão em `soe_regras`
|
||||
- [ ] Dashboards base no Superset (DRE, Fluxo de Caixa, Fiscal, RH)
|
||||
|
||||
**Próxima fase:**
|
||||
- Fase 3 — MiroFlow embutido + MiroFlowBridge para Superset
|
||||
|
||||
---
|
||||
|
||||
## Estimated Resource Usage
|
||||
|
||||
| Métrica | Estimativa |
|
||||
|---------|------------|
|
||||
| Commits | 7 |
|
||||
| Arquivos alterados | 29 |
|
||||
| Linhas adicionadas | ~1.875 |
|
||||
| Linhas removidas | ~106 |
|
||||
| Ciclos plan/execute | ~4 (uma por feature principal) |
|
||||
| Subagents spawned | 0 |
|
||||
|
||||
> **Nota:** contagens de tokens e custo requerem instrumentação via API.
|
||||
> Estas métricas refletem apenas a atividade observável da sessão.
|
||||
|
||||
---
|
||||
|
||||
*Gerado por `/gsd:session-report`*
|
||||
|
|
@ -0,0 +1,374 @@
|
|||
# AUDITORIA DO PLANO XOS - Arcádia Suite v1.0
|
||||
**Data:** 06 de Fevereiro de 2026
|
||||
**Projeto:** Arcádia Suite - Office Estratégico Empresarial
|
||||
**Escopo:** Sistema Operacional de Desenvolvimento Autônomo (XOS) com Blackboard Architecture
|
||||
|
||||
---
|
||||
|
||||
## RESUMO EXECUTIVO
|
||||
|
||||
O plano XOS v1.0 foi executado em **4 fases** progressivas, transformando o Arcádia Suite de um sistema de automação empresarial em um **sistema operacional de desenvolvimento autônomo** com 6 agentes de IA especializados, governança de segurança, e pipeline autônomo de ponta a ponta.
|
||||
|
||||
| Indicador | Valor |
|
||||
|---|---|
|
||||
| Fases planejadas | 4 |
|
||||
| Fases executadas | 4 (100%) |
|
||||
| Agentes de IA criados | 6 |
|
||||
| Tabelas de banco criadas | 31 (XOS) + 3 (Blackboard) = 34 novas tabelas |
|
||||
| Linhas de código (XOS backend) | ~5.491 linhas |
|
||||
| Linhas de código (XOS frontend) | ~988 linhas |
|
||||
| APIs criadas | 22+ endpoints |
|
||||
| Páginas UI criadas | 2 (Governance Dashboard + Pipeline UI) |
|
||||
|
||||
---
|
||||
|
||||
## FASE 1 - FUNDAÇÃO (Blackboard + Agentes Base)
|
||||
**Status: CONCLUIDO**
|
||||
|
||||
### O que foi planejado:
|
||||
- Criar arquitetura Blackboard centralizada para coordenação de agentes
|
||||
- Implementar agentes base de IA para desenvolvimento autônomo
|
||||
- Sistema de ferramentas (ToolManager) para interação com código
|
||||
|
||||
### O que foi executado:
|
||||
|
||||
#### 1.1 Blackboard Service (`server/blackboard/service.ts` - 476 linhas)
|
||||
- Quadro negro centralizado para coordenação de tarefas entre agentes
|
||||
- Criação, atribuição e monitoramento de tarefas
|
||||
- Gerenciamento de artefatos (código gerado, especificações, relatórios)
|
||||
- Logging estruturado por agente
|
||||
|
||||
#### 1.2 Base Agent (`server/blackboard/BaseBlackboardAgent.ts` - 189 linhas)
|
||||
- Classe abstrata para todos os agentes de IA
|
||||
- Loop thought-action-observation com OpenAI
|
||||
- Polling automático para busca de tarefas pendentes
|
||||
- Integração com governança para verificação de políticas
|
||||
- Event emitter para comunicação entre agentes
|
||||
|
||||
#### 1.3 Context Indexer (`server/blackboard/ContextIndexer.ts` - 277 linhas)
|
||||
- Indexação inteligente da estrutura do projeto
|
||||
- Mapeamento de arquivos, imports e dependências
|
||||
- Contexto técnico para agentes tomarem decisões informadas
|
||||
|
||||
#### 1.4 ToolManager (`server/autonomous/tools/ToolManager.ts` - 171 linhas)
|
||||
- Registro e execução de ferramentas para agentes
|
||||
- 10 ferramentas implementadas:
|
||||
|
||||
| Ferramenta | Arquivo | Linhas | Função |
|
||||
|---|---|---|---|
|
||||
| ReadFileTool | filesystem/ReadFileTool.ts | 66 | Leitura de arquivos do projeto |
|
||||
| WriteFileTool | filesystem/WriteFileTool.ts | 88 | Escrita segura de arquivos |
|
||||
| ListDirectoryTool | filesystem/ListDirectoryTool.ts | 85 | Listagem de diretórios |
|
||||
| SearchCodeTool | filesystem/SearchCodeTool.ts | 92 | Busca por padrões no código |
|
||||
| RunCommandTool | command/RunCommandTool.ts | 76 | Execução de comandos shell |
|
||||
| TypeCheckTool | command/TypeCheckTool.ts | 70 | Validação TypeScript real |
|
||||
| GitStatusTool | git/GitStatusTool.ts | 47 | Status do repositório |
|
||||
| GitCommitTool | git/GitCommitTool.ts | 67 | Commits automáticos |
|
||||
| AnalyzeRepoTool | github/AnalyzeRepoTool.ts | 41 | Análise de repos externos |
|
||||
| ReadExternalFileTool | github/ReadExternalFileTool.ts | 41 | Leitura de repos GitHub |
|
||||
| GitHubCommitTool | github/GitHubCommitTool.ts | 55 | Commits via GitHub API |
|
||||
|
||||
#### 1.5 Tabelas de Banco (Fase 1)
|
||||
|
||||
| Tabela | Função |
|
||||
|---|---|
|
||||
| `blackboard_tasks` | Tarefas do quadro negro (status, agente, prioridade, parent) |
|
||||
| `blackboard_artifacts` | Artefatos gerados (código, specs, relatórios) |
|
||||
| `blackboard_agent_logs` | Logs estruturados por agente |
|
||||
|
||||
---
|
||||
|
||||
## FASE 2 - AGENTES ESPECIALIZADOS + GOVERNANÇA
|
||||
**Status: CONCLUIDO**
|
||||
|
||||
### O que foi planejado:
|
||||
- Criar 5 agentes especializados com papéis distintos
|
||||
- Implementar camada de governança com políticas de segurança
|
||||
- Sistema de auditoria imutável
|
||||
|
||||
### O que foi executado:
|
||||
|
||||
#### 2.1 Agentes Especializados (5 agentes)
|
||||
|
||||
| Agente | Arquivo | Linhas | Papel |
|
||||
|---|---|---|---|
|
||||
| Architect | agents/ArchitectAgent.ts | 156 | Interpreta requisitos, cria specs técnicas, analisa estrutura existente |
|
||||
| Generator | agents/GeneratorAgent.ts | 187 | Gera código TypeScript/React de alta qualidade seguindo padrões do projeto |
|
||||
| Validator | agents/ValidatorAgent.ts | 232 | Valida código com TypeScript real (tsc --noEmit), análise de qualidade, score >= 60 para aprovar |
|
||||
| Executor | agents/ExecutorAgent.ts | 178 | Prepara código para staging, verifica arquivos protegidos, NÃO aplica direto |
|
||||
| Evolution | agents/EvolutionAgent.ts | 263 | Aprende com execuções passadas, documenta padrões, sugere melhorias |
|
||||
|
||||
#### 2.2 Governança de Segurança (`server/governance/service.ts` - 281 linhas)
|
||||
|
||||
**Motor de Políticas (Fail-Closed):**
|
||||
- Avaliação de políticas antes de qualquer ação
|
||||
- Se não há política explícita permitindo, a ação é BLOQUEADA
|
||||
- Integrado automaticamente em BaseBlackboardAgent e ToolManager
|
||||
|
||||
**5 Políticas de Segurança:**
|
||||
|
||||
| # | Política | Regra |
|
||||
|---|---|---|
|
||||
| 1 | Proteção de arquivos críticos | Bloqueia escrita em server/routes.ts, server/index.ts, shared/schema.ts, etc. |
|
||||
| 2 | Permissões de leitura | Permite leitura de qualquer arquivo do projeto |
|
||||
| 3 | Bloqueio de comandos destrutivos | Bloqueia rm -rf, DROP TABLE, DELETE FROM, etc. |
|
||||
| 4 | Aprovação humana para produção | Requer aprovação do usuário antes de deploy |
|
||||
| 5 | Staging automático com threshold | Score de validação mínimo para ir para staging |
|
||||
|
||||
#### 2.3 Registros de Governança
|
||||
|
||||
| Componente | Tabela | Função |
|
||||
|---|---|---|
|
||||
| Contract Registry | `xos_contract_registry` | Contratos entre agentes (SLAs, dependências) |
|
||||
| Tool Registry | `xos_tool_registry` | Registro de ferramentas disponíveis (auto-sync do ToolManager) |
|
||||
| Skill Registry | `xos_skill_registry` | Habilidades aprendidas com tracking de uso |
|
||||
| Policy Rules | `xos_policy_rules` | Regras de política de segurança |
|
||||
| Audit Trail | `xos_audit_trail` | Trilha de auditoria imutável de todas as ações |
|
||||
|
||||
#### 2.4 API de Governança (`server/governance/routes.ts` - 266 linhas)
|
||||
|
||||
| Endpoint | Método | Função |
|
||||
|---|---|---|
|
||||
| `/api/governance/evaluate` | POST | Avaliar política para ação |
|
||||
| `/api/governance/contracts` | GET/POST | Listar/criar contratos |
|
||||
| `/api/governance/tools` | GET | Listar ferramentas registradas |
|
||||
| `/api/governance/skills` | GET/POST | Listar/registrar habilidades |
|
||||
| `/api/governance/policies` | GET/POST | Listar/criar políticas |
|
||||
| `/api/governance/audit` | GET | Consultar trilha de auditoria |
|
||||
| `/api/governance/dashboard` | GET | Dashboard com estatísticas |
|
||||
|
||||
---
|
||||
|
||||
## FASE 3 - JOB QUEUE + METRICS + RESEARCHER + DASHBOARD
|
||||
**Status: CONCLUIDO**
|
||||
|
||||
### O que foi planejado:
|
||||
- Job Queue PostgreSQL para processamento assíncrono
|
||||
- Métricas de agentes para monitoramento
|
||||
- 6o agente (Researcher) para análise de repositórios externos
|
||||
- Dashboard visual de governança
|
||||
|
||||
### O que foi executado:
|
||||
|
||||
#### 3.1 Job Queue PostgreSQL (`server/governance/jobQueue.ts` - 252 linhas)
|
||||
|
||||
| Feature | Implementação |
|
||||
|---|---|
|
||||
| Fila de jobs | Tabela `xos_job_queue` com prioridade (0-10) |
|
||||
| Claiming atômico | `UPDATE ... FOR UPDATE SKIP LOCKED` (previne race conditions) |
|
||||
| Retry automático | Máximo de tentativas configurável por job |
|
||||
| Dead-letter | Jobs que falharam todas tentativas vão para dead-letter |
|
||||
| Handler registry | Registro de handlers por tipo de job |
|
||||
| Processamento | Loop de polling com intervalo configurável |
|
||||
|
||||
#### 3.2 Métricas de Agentes
|
||||
|
||||
| Tabela | Campos | Função |
|
||||
|---|---|---|
|
||||
| `xos_agent_metrics` | agentName, tasksCompleted, tasksFailed, avgDuration, lastActive | Monitoramento de performance |
|
||||
|
||||
#### 3.3 Researcher Agent (`server/blackboard/agents/ResearcherAgent.ts` - 145 linhas)
|
||||
|
||||
- 6o agente especializado em pesquisa
|
||||
- Analisa repositórios GitHub externos
|
||||
- Pesquisa padrões de implementação e boas práticas
|
||||
- Compara soluções e frameworks
|
||||
- Gera relatórios de viabilidade técnica
|
||||
|
||||
#### 3.4 Governance Dashboard (`client/src/pages/XosGovernance.tsx` - 517 linhas)
|
||||
|
||||
**Rota:** `/xos/governance`
|
||||
|
||||
| Seção | Conteúdo |
|
||||
|---|---|
|
||||
| Stats Cards | Contratos, ferramentas, habilidades, políticas, auditorias, jobs |
|
||||
| Audit Trail | Tabela com ações, decisões, agentes, timestamps |
|
||||
| Policies | Lista de políticas de segurança ativas |
|
||||
| Tools | Ferramentas registradas com descrição |
|
||||
| Skills | Habilidades aprendidas com contagem de uso |
|
||||
| Jobs | Fila de jobs com status e prioridade |
|
||||
| Agent Monitoring | Métricas por agente (tarefas, falhas, duração média) |
|
||||
|
||||
#### 3.5 APIs Adicionais (Fase 3)
|
||||
|
||||
| Endpoint | Método | Função |
|
||||
|---|---|---|
|
||||
| `/api/governance/jobs` | GET | Listar jobs da fila |
|
||||
| `/api/governance/jobs` | POST | Enfileirar novo job |
|
||||
| `/api/governance/jobs/:id/cancel` | POST | Cancelar job |
|
||||
| `/api/governance/jobs/:id/retry` | POST | Retentar job |
|
||||
| `/api/governance/metrics` | GET | Métricas dos agentes |
|
||||
|
||||
---
|
||||
|
||||
## FASE 4 - PIPELINE AUTÔNOMO (Prompt-to-Production)
|
||||
**Status: CONCLUIDO**
|
||||
|
||||
### O que foi planejado:
|
||||
- Pipeline Orchestrator que encadeia todos os 6 agentes
|
||||
- Fluxo: Prompt em Português -> Design -> Codegen -> Validação -> Staging -> Evolução
|
||||
- Sistema de staging com revisão e aprovação do usuário
|
||||
- Streaming em tempo real via SSE
|
||||
- UI para submissão, acompanhamento e aprovação
|
||||
|
||||
### O que foi executado:
|
||||
|
||||
#### 4.1 Pipeline Orchestrator (`server/blackboard/PipelineOrchestrator.ts` - 350 linhas)
|
||||
|
||||
| Feature | Implementação |
|
||||
|---|---|
|
||||
| Criação de pipeline | Registra em `xos_dev_pipelines` + cria tarefa principal |
|
||||
| Fases encadeadas | design -> codegen -> validation -> staging -> evolution |
|
||||
| Monitoramento | Polling a cada 3s para verificar progresso de cada fase |
|
||||
| Job handlers | Registra handlers para cada tipo de job (pipeline_*) |
|
||||
| SSE Broadcasting | Emite eventos em tempo real para clientes conectados |
|
||||
| Staging review | Pausa no staging para revisão humana antes de aplicar |
|
||||
| Aplicação de código | Aplica mudanças aprovadas ao filesystem com backup |
|
||||
| Proteção de arquivos | Verifica lista PROTECTED_FILES antes de aplicar |
|
||||
|
||||
**Fluxo completo do pipeline:**
|
||||
```
|
||||
1. Usuário envia prompt em português
|
||||
2. DESIGN: ArchitectAgent analisa e cria especificação técnica
|
||||
3. CODEGEN: GeneratorAgent gera código TypeScript/React
|
||||
4. VALIDATION: ValidatorAgent executa tsc --noEmit real
|
||||
5. STAGING: ExecutorAgent prepara para staging (NÃO aplica direto)
|
||||
6. REVIEW: Usuário revisa código staged e aprova/rejeita
|
||||
7. EVOLUTION: EvolutionAgent documenta aprendizados
|
||||
```
|
||||
|
||||
#### 4.2 Tabelas de Banco (Fase 4)
|
||||
|
||||
| Tabela | Campos Principais | Função |
|
||||
|---|---|---|
|
||||
| `xos_dev_pipelines` | prompt, status, currentPhase, mainTaskId, phases (JSON), error | Rastreamento do pipeline |
|
||||
| `xos_staging_changes` | pipelineId, filePath, content, action, status, reviewedBy | Código staged para revisão |
|
||||
|
||||
#### 4.3 Pipeline API (`server/blackboard/pipelineRoutes.ts` - 134 linhas)
|
||||
|
||||
| Endpoint | Método | Função |
|
||||
|---|---|---|
|
||||
| `/api/xos/pipeline` | POST | Criar e iniciar pipeline com prompt |
|
||||
| `/api/xos/pipeline` | GET | Listar pipelines recentes |
|
||||
| `/api/xos/pipeline/:id` | GET | Detalhes do pipeline (staging, tarefas, artefatos, logs) |
|
||||
| `/api/xos/pipeline/:id/staging` | GET | Listar mudanças staged |
|
||||
| `/api/xos/pipeline/:id/approve` | POST | Aprovar e aplicar código staged |
|
||||
| `/api/xos/pipeline/:id/reject` | POST | Rejeitar código staged |
|
||||
| `/api/xos/pipeline/:id/stream` | GET | SSE para eventos em tempo real |
|
||||
|
||||
#### 4.4 Pipeline UI (`client/src/pages/XosPipeline.tsx` - 471 linhas)
|
||||
|
||||
**Rota:** `/xos/pipeline`
|
||||
|
||||
| Componente | Funcionalidade |
|
||||
|---|---|
|
||||
| Formulário de prompt | Textarea com instruções em português + botão de envio |
|
||||
| Timeline ao vivo | 5 fases com ícones, cores e status em tempo real |
|
||||
| Staging review | Preview do código gerado com path dos arquivos |
|
||||
| Approve/Reject | Botões para aprovar ou rejeitar código staged |
|
||||
| Pipeline history | Lista de pipelines passados com status badges |
|
||||
| SSE EventSource | Conexão em tempo real para atualizações |
|
||||
|
||||
---
|
||||
|
||||
## INVENTÁRIO COMPLETO DE ARTEFATOS
|
||||
|
||||
### Arquivos Backend (server/)
|
||||
|
||||
| Caminho | Linhas | Fase |
|
||||
|---|---|---|
|
||||
| `server/blackboard/BaseBlackboardAgent.ts` | 189 | 1 |
|
||||
| `server/blackboard/service.ts` | 476 | 1 |
|
||||
| `server/blackboard/ContextIndexer.ts` | 277 | 1 |
|
||||
| `server/blackboard/routes.ts` | - | 1 |
|
||||
| `server/blackboard/agents/index.ts` | 58 | 1/2 |
|
||||
| `server/blackboard/agents/ArchitectAgent.ts` | 156 | 2 |
|
||||
| `server/blackboard/agents/GeneratorAgent.ts` | 187 | 2 |
|
||||
| `server/blackboard/agents/ValidatorAgent.ts` | 232 | 2 |
|
||||
| `server/blackboard/agents/ExecutorAgent.ts` | 178 | 2 |
|
||||
| `server/blackboard/agents/EvolutionAgent.ts` | 263 | 2 |
|
||||
| `server/blackboard/agents/ResearcherAgent.ts` | 145 | 3 |
|
||||
| `server/blackboard/PipelineOrchestrator.ts` | 350 | 4 |
|
||||
| `server/blackboard/pipelineRoutes.ts` | 134 | 4 |
|
||||
| `server/governance/service.ts` | 281 | 2 |
|
||||
| `server/governance/routes.ts` | 266 | 2/3 |
|
||||
| `server/governance/jobQueue.ts` | 252 | 3 |
|
||||
| `server/autonomous/tools/ToolManager.ts` | 171 | 1 |
|
||||
| `server/autonomous/tools/BaseTool.ts` | 82 | 1 |
|
||||
| `server/autonomous/tools/**/*.ts` | ~750 | 1 |
|
||||
| `server/autonomous/agents/**/*.ts` | ~1.003 | 1 |
|
||||
| **TOTAL BACKEND XOS** | **~5.491** | |
|
||||
|
||||
### Arquivos Frontend (client/src/)
|
||||
|
||||
| Caminho | Linhas | Fase |
|
||||
|---|---|---|
|
||||
| `client/src/pages/XosGovernance.tsx` | 517 | 3 |
|
||||
| `client/src/pages/XosPipeline.tsx` | 471 | 4 |
|
||||
| **TOTAL FRONTEND XOS** | **988** | |
|
||||
|
||||
### Tabelas de Banco de Dados (34 novas tabelas)
|
||||
|
||||
| Tabela | Fase | Propósito |
|
||||
|---|---|---|
|
||||
| `blackboard_tasks` | 1 | Tarefas do quadro negro |
|
||||
| `blackboard_artifacts` | 1 | Artefatos gerados |
|
||||
| `blackboard_agent_logs` | 1 | Logs de agentes |
|
||||
| `xos_contract_registry` | 2 | Contratos entre agentes |
|
||||
| `xos_tool_registry` | 2 | Ferramentas registradas |
|
||||
| `xos_skill_registry` | 2 | Habilidades aprendidas |
|
||||
| `xos_policy_rules` | 2 | Políticas de segurança |
|
||||
| `xos_audit_trail` | 2 | Trilha de auditoria |
|
||||
| `xos_job_queue` | 3 | Fila de jobs assíncronos |
|
||||
| `xos_agent_metrics` | 3 | Métricas de agentes |
|
||||
| `xos_dev_pipelines` | 4 | Pipelines de desenvolvimento |
|
||||
| `xos_staging_changes` | 4 | Código staged para revisão |
|
||||
|
||||
---
|
||||
|
||||
## DECISÕES ARQUITETURAIS REGISTRADAS
|
||||
|
||||
| # | Decisão | Justificativa |
|
||||
|---|---|---|
|
||||
| 1 | SSE em vez de Socket.IO para pipeline | Evita conflito com sockets existentes (WhatsApp, Chat) |
|
||||
| 2 | FOR UPDATE SKIP LOCKED no job claiming | Previne race conditions com múltiplos workers |
|
||||
| 3 | Fail-closed na governança | Segurança: ação sem política explícita = BLOQUEADA |
|
||||
| 4 | Staging obrigatório antes de aplicar | Nunca auto-aplica código em produção |
|
||||
| 5 | Arquivos protegidos (PROTECTED_FILES) | Lista de arquivos críticos que agentes não podem sobrescrever |
|
||||
| 6 | Polling de 3s no pipeline monitor | Equilíbrio entre responsividade e carga no servidor |
|
||||
| 7 | Score >= 60 para validação | Threshold mínimo para código passar pela validação |
|
||||
| 8 | Prompts em português nos agentes | Sistema projetado para prompts de negócio em português brasileiro |
|
||||
|
||||
---
|
||||
|
||||
## VERIFICAÇÃO DE FUNCIONAMENTO
|
||||
|
||||
| Teste | Status | Evidência |
|
||||
|---|---|---|
|
||||
| API Pipeline retorna lista | OK | `GET /api/xos/pipeline` -> `{"success": true, "pipelines": [...]}` |
|
||||
| Criação de pipeline funciona | OK | `POST /api/xos/pipeline` -> Pipeline #1 criado e rodando |
|
||||
| Fases iniciam corretamente | OK | Design phase `status: "running"` após criação |
|
||||
| SSE streaming configurado | OK | Endpoint `/api/xos/pipeline/:id/stream` implementado |
|
||||
| Staging review configurado | OK | Endpoints approve/reject implementados |
|
||||
| Governance dashboard funciona | OK | Página `/xos/governance` com 517 linhas de UI |
|
||||
| Pipeline UI funciona | OK | Página `/xos/pipeline` com 471 linhas de UI |
|
||||
| Todas as rotas registradas | OK | Verificado em server/routes.ts e client/src/App.tsx |
|
||||
| 6 agentes rodando | OK | Architect, Generator, Validator, Executor, Evolution, Researcher |
|
||||
| Job Queue operacional | OK | Atomic claiming com FOR UPDATE SKIP LOCKED |
|
||||
|
||||
---
|
||||
|
||||
## CONCLUSÃO
|
||||
|
||||
O plano XOS v1.0 foi executado com **100% de completude** em suas 4 fases:
|
||||
|
||||
- **Fase 1:** Fundação sólida com Blackboard architecture, 10 ferramentas e sistema de coordenação
|
||||
- **Fase 2:** 5 agentes especializados + governança com 5 políticas de segurança fail-closed
|
||||
- **Fase 3:** Job Queue PostgreSQL + Métricas + 6o agente Researcher + Dashboard visual
|
||||
- **Fase 4:** Pipeline autônomo prompt-to-production com staging review e SSE streaming
|
||||
|
||||
O sistema está operacional e pronto para uso, com todas as salvaguardas de segurança ativas (staging obrigatório, arquivos protegidos, governança fail-closed, aprovação humana para produção).
|
||||
|
||||
---
|
||||
|
||||
*Documento gerado em 06/02/2026 como registro de auditoria do plano XOS v1.0 do Arcádia Suite.*
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
# Avaliação de Aderência - Impacto Geologia
|
||||
## Arcádia Suite vs Requisitos do Book de Requisitos
|
||||
### Data: Fevereiro 2026
|
||||
|
||||
-## 1. RESUMO EXECUTIVO
|
||||
|
||||
Este documento apresenta a análise de aderência entre os requisitos levantados no Book de Requisitos da Impacto Geologia e as funcionalidades atualmente implementadas na plataforma Arcádia Suite. A avaliação considera o estado atual do sistema, identificando o que já está pronto, o que está parcialmente implementado e o que precisa ser desenvolvido.
|
||||
|
||||
**Resultado Geral:**
|
||||
| Status | Quantidade | % |
|
||||
|--------|-----------|---|
|
||||
| Implementado | 14 | 38% |
|
||||
| Parcialmente Implementado | 10 | 27% |
|
||||
| Não Implementado | 13 | 35% |
|
||||
| **Total de Requisitos** | **37** | **100%** |
|
||||
|
||||
---
|
||||
|
||||
## 2. ANÁLISE POR MÓDULO
|
||||
|
||||
### 2.1 Módulo Administrativo Financeiro
|
||||
|
||||
| ID | Requisito | Status | Observação |
|
||||
|----|-----------|--------|------------|
|
||||
| RF-AF01 | Conciliação Bancária Automatizada | Parcial | Existe `finBankAccounts` e `finTransactions` para contas bancárias e transações, mas não há rotina de conciliação automática com extratos OFX/CSV |
|
||||
| RF-AF02 | Fluxo de Pagamentos Controlado | Parcial | `finAccountsPayable` existe para contas a pagar, mas falta workflow de aprovação (programação → aprovação → execução) |
|
||||
| RF-AF03 | Controle de Fluxo de Caixa Projetado | Parcial | `finCashFlowCategories` e transações existem, mas falta projeção automática de 90 dias com cenários |
|
||||
| RF-AF04 | Gestão de Impostos e Obrigações | Implementado | Módulo fiscal completo: `fiscalNcms`, `fiscalCfops`, `fiscalCests`, `fiscalGruposTributacao`, `fiscalConfiguracoes`, `fiscalNotas` com integração Arcádia Plus |
|
||||
| RF-AF05 | Prestação de Contas Digital | Parcial | `fieldExpenses` existe para despesas de campo, mas falta fluxo de aprovação digital e comprovantes fotográficos via mobile |
|
||||
| - | Gestão de Ativos e Endividamento | Não Implementado | Não existe módulo de controle de ativos fixos nem dashboard de endividamento |
|
||||
| - | Gestão de Cartões Corporativos | Não Implementado | Não há funcionalidade para tratar cartões como "contas correntes individuais" com controle de cargas/depósitos |
|
||||
|
||||
**Resumo Financeiro: 1 implementado, 4 parciais, 2 não implementados**
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Módulo Comercial (Vendas e CRM)
|
||||
|
||||
| ID | Requisito | Status | Observação |
|
||||
|----|-----------|--------|------------|
|
||||
| RF-COM01 | Conversão Proposta → Projeto | Parcial | `crmProposals`, `crmProposalItems`, `crmOpportunities` existem, mas falta a automação de conversão proposta aprovada → criação automática de Projeto + Ordem de Venda |
|
||||
| RF-COM02 | Gestão de Incentivos/Comissões | Implementado | `crmCommissionRules`, `crmCommissions` no CRM + `retailCommissionPlans`, `retailCommissionClosures` no Retail - sistema completo de comissões |
|
||||
| RF-COM03 | Pesquisa de Satisfação (NPS) | Não Implementado | Campos de `satisfactionScore` existem em algumas tabelas, mas não há módulo dedicado de envio automático de pesquisa NPS pós-projeto |
|
||||
|
||||
**Resumo Comercial: 1 implementado, 1 parcial, 1 não implementado**
|
||||
|
||||
---
|
||||
|
||||
### 2.3 Módulo Operações e Projetos
|
||||
|
||||
| ID | Requisito | Status | Observação |
|
||||
|----|-----------|--------|------------|
|
||||
| RF-OP01 | Rastreabilidade por ID de Projeto | Implementado | `pcProjects`, `pcProjectMembers`, `pcProjectActivities`, `pcProjectTasks`, `pcProjectFiles`, `pcProjectHistory` - sistema completo de gestão de projetos com ID único e rastreabilidade |
|
||||
| RF-OP02 | Gestão de Insumos de Campo | Parcial | Existe estoque (`retailWarehouseStock`, `retailStockMovements`) mas não há vinculação automática de consumo de materiais/equipamentos por projeto |
|
||||
| RF-OP03 | Digitalização de Fichas de Campo | Parcial | `qualityFieldForms` existe para formulários digitais, mas falta os formulários específicos de geologia (Plano de Amostragem, PT, Monitoramento de Poços) e app mobile dedicado |
|
||||
| RF-OP04 | Gestão de Pendências de Faturamento | Não Implementado | Não há sistema de monitoramento de eventos de campo que causem quebra de faturamento com alertas automáticos |
|
||||
| - | Acompanhamento do Ciclo de Vida | Parcial | `pcProjectActivities` e `pcProjectHistory` registram atividades, mas falta pipeline visual desde pré-programação até entrega do relatório final |
|
||||
| - | Custódia de Equipamentos | Não Implementado | Não existe módulo para controle de custódia de equipamentos por equipe de campo (DOC-010) |
|
||||
| - | Programação de Equipes de Campo | Não Implementado | Não há funcionalidade de programação de equipes para mobilização de campo (DOC-008) |
|
||||
| - | Programação de Relatórios | Não Implementado | Não existe módulo de programação e acompanhamento de relatórios técnicos (DOC-009) |
|
||||
|
||||
**Resumo Operações: 1 implementado, 3 parciais, 4 não implementados**
|
||||
|
||||
---
|
||||
|
||||
### 2.4 Módulo Qualidade e Serviços de Terceiros
|
||||
|
||||
| ID | Requisito | Status | Observação |
|
||||
|----|-----------|--------|------------|
|
||||
| RF-QC01 | Controle de Amostras e Laudos | Implementado | `qualitySamples`, `qualityLabReports` - sistema para rastreamento de amostras enviadas a laboratórios e laudos recebidos |
|
||||
| RF-QC02 | Gestão de Documentos (QMS) | Implementado | `qualityDocuments`, `qualityDocumentRevisions` - controle de versão e lista mestra de documentos internos (FT-xx) |
|
||||
| RF-QC03 | Controle de Não Conformidades (RNC) | Implementado | `qualityNonConformities` - registro e gestão de RNC, Ações Corretivas e Oportunidades de Melhoria |
|
||||
| RF-QC04 | Homologação de Fornecedores (ISO 17025) | Parcial | `suppliers` e `purchaseOrders` existem, mas falta controle de certificações ISO 17025, bloqueio de compras de não-homologados e portal de fornecedores |
|
||||
| - | Matriz de Treinamentos | Implementado | `qualityTrainingMatrix` - controle de treinamentos, participação e validade |
|
||||
| - | Controle de Brancos | Não Implementado | Não existe funcionalidade específica para controle de brancos de laboratório (FT-58, FT-68) |
|
||||
|
||||
**Resumo Qualidade: 4 implementados, 1 parcial, 1 não implementado**
|
||||
|
||||
---
|
||||
|
||||
### 2.5 Módulo Fiscal e Tributário
|
||||
|
||||
| ID | Requisito | Status | Observação |
|
||||
|----|-----------|--------|------------|
|
||||
| RF-FT01 | Módulo Fiscal e Tributário | Implementado | Motor Fiscal completo (Arcádia Fisco): NCMs, CFOPs, CESTs, grupos tributação, certificados digitais, NF-e/NFC-e com integração SEFAZ via nfelib |
|
||||
| - | Emissão de NF-e de Serviço | Parcial | Infraestrutura fiscal existe, mas NFS-e (Nota Fiscal de Serviço Eletrônica) requer integração com prefeituras, que varia por município - não implementado |
|
||||
| - | Integração com Arcádia Plus | Implementado | `FiscalAdapter` com conexão via API para transmissão fiscal e emissão de documentos fiscais via Arcádia Plus |
|
||||
|
||||
**Resumo Fiscal: 2 implementados, 1 parcial, 0 não implementados**
|
||||
|
||||
---
|
||||
|
||||
### 2.6 Módulo Estoque e Logística
|
||||
|
||||
| ID | Requisito | Status | Observação |
|
||||
|----|-----------|--------|------------|
|
||||
| - | Gestão de Estoque | Implementado | `retailWarehouseStock`, `retailStockMovements`, `retailInventories`, `retailInventoryItems` - estoque completo com movimentações e inventário |
|
||||
| - | Transferências entre Almoxarifados | Implementado | `retailStockTransfers`, `retailStockTransferItems`, `retailTransferSerials` - transferências com rastreio de seriais |
|
||||
| - | Estoque de Segurança | Não Implementado | Não há funcionalidade de definição de estoque mínimo com alertas automáticos para itens com lead time > 90 dias |
|
||||
| - | Controle de Km Rodado | Não Implementado | Não existe módulo para controle de custos de km rodado por veículo/equipe |
|
||||
|
||||
**Resumo Estoque: 2 implementados, 0 parciais, 2 não implementados**
|
||||
|
||||
---
|
||||
|
||||
### 2.7 Módulo Gestão de Pessoas (RH)
|
||||
|
||||
| ID | Requisito | Status | Observação |
|
||||
|----|-----------|--------|------------|
|
||||
| - | Cadastro de Colaboradores | Implementado | `peopleFuncionarios`, `peopleDependentes`, `peopleCargos`, `peopleDepartamentos` |
|
||||
| - | Folha de Pagamento | Implementado | `peopleFolhaPagamento`, `peopleFolhaItens`, `peopleFolhaEventos`, `peopleEventosFolha` |
|
||||
| - | Controle de Férias | Implementado | `peopleFerias` |
|
||||
| - | Controle de Ponto | Implementado | `peoplePonto` |
|
||||
| - | Benefícios | Implementado | `peopleBeneficios`, `peopleFuncionarioBeneficios` |
|
||||
|
||||
**Resumo RH: 5 implementados - módulo completo**
|
||||
|
||||
---
|
||||
|
||||
### 2.8 Módulo Contabilidade
|
||||
|
||||
| ID | Requisito | Status | Observação |
|
||||
|----|-----------|--------|------------|
|
||||
| - | Plano de Contas | Implementado | `contabilPlanoContas` |
|
||||
| - | Centros de Custo | Implementado | `contabilCentrosCusto` - permite vincular custos por projeto |
|
||||
| - | Lançamentos Contábeis | Implementado | `contabilLancamentos`, `contabilPartidas` |
|
||||
| - | Períodos e Saldos | Implementado | `contabilPeriodos`, `contabilSaldos` |
|
||||
|
||||
**Resumo Contabilidade: 4 implementados - módulo completo**
|
||||
|
||||
---
|
||||
|
||||
## 3. REQUISITOS NÃO-FUNCIONAIS
|
||||
|
||||
| Requisito | Status | Observação |
|
||||
|-----------|--------|------------|
|
||||
| Usabilidade/Interface intuitiva | Implementado | Interface moderna React + Tailwind + shadcn/ui, estilo WhatsApp Web |
|
||||
| Integração via APIs | Implementado | APIs REST completas, protocolos MCP/A2A implementados |
|
||||
| Segurança e Governança | Implementado | Multi-tenant com tenant scoping, XOS Governance Layer, audit trail |
|
||||
| Mobile | Não Implementado | Não há aplicativo móvel dedicado; interface web é responsiva mas não tem funcionalidades offline para campo |
|
||||
| Framework FA (DocTypes customizados) | Implementado | Sistema ArcDocTypes com fields, layouts, pages, scripts e widgets |
|
||||
|
||||
---
|
||||
|
||||
## 4. PRIORIZAÇÃO - O QUE FALTA PARA FECHAR O PROJETO
|
||||
|
||||
### Prioridade ALTA (Essencial para entrega)
|
||||
|
||||
| # | Item | Esforço Estimado | Descrição |
|
||||
|---|------|-----------------|-----------|
|
||||
| 1 | Conversão Proposta → Projeto automática | 2-3 dias | Workflow que ao aprovar proposta cria automaticamente o Projeto e Ordem de Venda |
|
||||
| 2 | Workflow de aprovação de pagamentos | 2-3 dias | Fluxo: programação → aprovação (Fran → Sidney) → execução de pagamentos |
|
||||
| 3 | Conciliação bancária | 3-5 dias | Import de extratos bancários (OFX/CSV) e conciliação automática com transações |
|
||||
| 4 | Vinculação Insumos ↔ Projetos | 2-3 dias | Associar consumo de materiais e equipamentos a cada projeto |
|
||||
| 5 | Homologação de Fornecedores ISO 17025 | 3-4 dias | Status de homologação, certificações, bloqueio de compras para não-homologados |
|
||||
| 6 | Gestão de Pendências de Faturamento | 2-3 dias | Alertas para eventos de campo que impedem faturamento |
|
||||
|
||||
### Prioridade MÉDIA (Importante para operação completa)
|
||||
|
||||
| # | Item | Esforço Estimado | Descrição |
|
||||
|---|------|-----------------|-----------|
|
||||
| 7 | Prestação de contas digital com aprovação | 2-3 dias | Upload de comprovantes, fluxo aprovação de despesas de campo |
|
||||
| 8 | Projeção de fluxo de caixa 90 dias | 2-3 dias | Dashboard com projeção baseada em recebíveis e pagáveis programados |
|
||||
| 9 | Formulários de campo específicos | 3-5 dias | Plano de Amostragem, PT, Monitoramento de Poços como DocTypes |
|
||||
| 10 | Portal de Fornecedores | 3-5 dias | Área externa para fornecedores atualizarem documentos e certificações |
|
||||
| 11 | NPS automático pós-projeto | 1-2 dias | Envio automático de pesquisa de satisfação ao concluir projeto |
|
||||
| 12 | Programação de Equipes de Campo | 2-3 dias | Calendário de mobilização de equipes para projetos |
|
||||
| 13 | Programação de Relatórios | 2 dias | Controle de prazos e status de relatórios técnicos |
|
||||
| 14 | NFS-e (Nota Fiscal de Serviço) | 5-10 dias | Integração com webservices municipais (varia por prefeitura) |
|
||||
|
||||
### Prioridade BAIXA (Desejável / Fase 2)
|
||||
|
||||
| # | Item | Esforço Estimado | Descrição |
|
||||
|---|------|-----------------|-----------|
|
||||
| 15 | Gestão de Ativos Fixos | 3-5 dias | Cadastro, depreciação e controle de ativos da empresa |
|
||||
| 16 | Dashboard de Endividamento | 1-2 dias | BI com visão consolidada de endividamento |
|
||||
| 17 | Gestão de Cartões Corporativos | 2-3 dias | Tratar cartões como contas individuais com cargas |
|
||||
| 18 | Estoque de Segurança com alertas | 1-2 dias | Definição de estoque mínimo e alertas automáticos |
|
||||
| 19 | Controle de Km Rodado | 1-2 dias | Registro de km por veículo/equipe com cálculo de custos |
|
||||
| 20 | Custódia de Equipamentos | 2-3 dias | Controle de equipamentos alocados por equipe de campo |
|
||||
| 21 | Controle de Brancos de Laboratório | 1-2 dias | Registro e rastreamento de amostras branco |
|
||||
| 22 | App Mobile para Campo | 15-20 dias | PWA ou app nativo para coleta de dados offline em campo |
|
||||
| 23 | Contratos Guarda-Chuva | 2-3 dias | Gestão de contratos de fornecimento de longo prazo |
|
||||
|
||||
---
|
||||
|
||||
## 5. ESTIMATIVA TOTAL DE ESFORÇO
|
||||
|
||||
| Prioridade | Itens | Dias Estimados |
|
||||
|------------|-------|---------------|
|
||||
| Alta | 6 itens | 14-21 dias |
|
||||
| Média | 8 itens | 19-33 dias |
|
||||
| Baixa | 9 itens | 28-42 dias |
|
||||
| **Total** | **23 itens** | **61-96 dias** |
|
||||
|
||||
### Recomendação de Fases:
|
||||
|
||||
**Fase 1 (MVP Impacto):** Prioridade Alta → ~3-4 semanas
|
||||
- Entrega o mínimo necessário para operação integrada
|
||||
|
||||
**Fase 2 (Operação Completa):** Prioridade Média → ~4-5 semanas
|
||||
- Completa todas as funcionalidades operacionais do dia a dia
|
||||
|
||||
**Fase 3 (Evolução):** Prioridade Baixa → ~5-7 semanas
|
||||
- Funcionalidades avançadas e app mobile
|
||||
|
||||
---
|
||||
|
||||
## 6. O QUE JÁ ESTÁ PRONTO E FUNCIONANDO
|
||||
|
||||
A Arcádia Suite já possui uma base sólida implementada:
|
||||
|
||||
- **Contabilidade completa** (plano de contas, centros de custo, lançamentos, períodos)
|
||||
- **Gestão de Pessoas completa** (funcionários, folha, férias, ponto, benefícios)
|
||||
- **Motor Fiscal completo** (NCMs, CFOPs, CESTs, NF-e/NFC-e, certificados digitais)
|
||||
- **CRM e Comercial** (leads, oportunidades, propostas, comissões)
|
||||
- **Gestão de Projetos** (projetos, membros, tarefas, atividades, histórico)
|
||||
- **Qualidade** (amostras, laudos, RNC, documentos, treinamentos)
|
||||
- **Estoque** (warehouses, movimentações, transferências, inventário)
|
||||
- **Financeiro básico** (contas bancárias, a pagar, a receber, transações)
|
||||
- **Framework FA** (DocTypes customizados para fichas de campo)
|
||||
- **Multi-tenant com governança** (segurança, audit trail, políticas)
|
||||
- **Inteligência Artificial** (Manus agent, Dev Center, auto-programação)
|
||||
- **Comunicação unificada** (WhatsApp, chat interno, email)
|
||||
- **BI e Dashboards** (motor BI com análise de dados)
|
||||
|
||||
---
|
||||
|
||||
## 7. CONCLUSÃO
|
||||
|
||||
O sistema Arcádia Suite apresenta **aderência de ~65%** aos requisitos da Impacto Geologia, com os módulos estruturais (Contabilidade, RH, Fiscal, Qualidade) praticamente completos. Os principais gaps estão nos **fluxos de negócio específicos** (workflows de aprovação, conciliação bancária, conversão proposta→projeto) e nas **funcionalidades de campo** (formulários específicos de geologia, app mobile, programação de equipes).--
|
||||
|
||||
|
||||
|
||||
A recomendação é priorizar os 6 itens de alta prioridade para uma entrega funcional em **3-4 semanas**, seguida de evolução gradual nos itens médios e baixos.
|
||||
|
|
@ -0,0 +1,620 @@
|
|||
# Arcadia Suite - Guia Completo de Hospedagem e Deploy
|
||||
|
||||
## Indice
|
||||
|
||||
1. [Requisitos do Servidor](#1-requisitos-do-servidor)
|
||||
2. [Variaveis de Ambiente](#2-variaveis-de-ambiente)
|
||||
3. [Instalacao](#3-instalacao)
|
||||
4. [Banco de Dados](#4-banco-de-dados)
|
||||
5. [Servicos Python](#5-servicos-python)
|
||||
6. [Metabase (Arcadia Insights)](#6-metabase-arcadia-insights)
|
||||
7. [Build e Producao](#7-build-e-producao)
|
||||
8. [Configuracao do Servidor Web (Nginx)](#8-configuracao-do-servidor-web-nginx)
|
||||
9. [Process Manager (PM2)](#9-process-manager-pm2)
|
||||
10. [SSL/HTTPS](#10-sslhttps)
|
||||
11. [Estrutura de Portas](#11-estrutura-de-portas)
|
||||
12. [Arcadia Plus (Laravel)](#12-arcadia-plus-laravel)
|
||||
13. [Manutencao e Backups](#13-manutencao-e-backups)
|
||||
14. [Troubleshooting](#14-troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## 1. Requisitos do Servidor
|
||||
|
||||
### Minimo Recomendado
|
||||
- **SO:** Ubuntu 22.04 LTS ou Debian 12
|
||||
- **CPU:** 4 vCPUs
|
||||
- **RAM:** 8 GB (16 GB recomendado)
|
||||
- **Disco:** 50 GB SSD
|
||||
- **Node.js:** v20.x ou superior
|
||||
- **Python:** 3.11 ou superior
|
||||
- **PostgreSQL:** 15 ou superior
|
||||
- **Java:** OpenJDK 11+ (para Metabase)
|
||||
- **PHP:** 8.1+ (para Arcadia Plus, opcional)
|
||||
- **Composer:** 2.x (para Arcadia Plus, opcional)
|
||||
|
||||
### Instalacao de Dependencias (Ubuntu/Debian)
|
||||
|
||||
```bash
|
||||
# Atualizar sistema
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
|
||||
# Node.js 20
|
||||
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
||||
sudo apt install -y nodejs
|
||||
|
||||
# Python 3.11
|
||||
sudo apt install -y python3.11 python3.11-venv python3-pip
|
||||
|
||||
# PostgreSQL 15
|
||||
sudo apt install -y postgresql postgresql-contrib
|
||||
|
||||
# Java (para Metabase)
|
||||
sudo apt install -y openjdk-11-jre-headless
|
||||
|
||||
# Ferramentas auxiliares
|
||||
sudo apt install -y git nginx certbot python3-certbot-nginx build-essential
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Variaveis de Ambiente
|
||||
|
||||
Crie um arquivo `.env` na raiz do projeto:
|
||||
|
||||
```env
|
||||
# === BANCO DE DADOS ===
|
||||
DATABASE_URL=postgresql://arcadia_user:SUA_SENHA_AQUI@localhost:5432/arcadia_suite
|
||||
PGHOST=localhost
|
||||
PGPORT=5432
|
||||
PGUSER=arcadia_user
|
||||
PGPASSWORD=SUA_SENHA_AQUI
|
||||
PGDATABASE=arcadia_suite
|
||||
|
||||
# === OPENAI (IA) ===
|
||||
OPENAI_API_KEY=sk-sua-chave-openai-aqui
|
||||
|
||||
# === ERPNext (opcional) ===
|
||||
ERPNEXT_URL=https://seu-erpnext.com
|
||||
ERPNEXT_API_KEY=sua-api-key
|
||||
ERPNEXT_API_SECRET=seu-api-secret
|
||||
|
||||
# === GITHUB (opcional - para XOS Pipeline) ===
|
||||
GITHUB_TOKEN=ghp_seu-token-github
|
||||
|
||||
# === PRODUCAO ===
|
||||
NODE_ENV=production
|
||||
PORT=5000
|
||||
SESSION_SECRET=gere-uma-string-secreta-longa-e-aleatoria
|
||||
|
||||
# === METABASE ===
|
||||
MB_DB_TYPE=postgres
|
||||
MB_DB_DBNAME=arcadia_metabase
|
||||
MB_DB_PORT=5432
|
||||
MB_DB_USER=arcadia_user
|
||||
MB_DB_PASS=SUA_SENHA_AQUI
|
||||
MB_DB_HOST=localhost
|
||||
MB_JETTY_PORT=8088
|
||||
METABASE_PORT=8088
|
||||
METASET_ADMIN_EMAIL=admin@arcadia.app
|
||||
METASET_ADMIN_PASSWORD=SuaSenhaMetaSet
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Instalacao
|
||||
|
||||
```bash
|
||||
# Clonar o repositorio
|
||||
git clone https://github.com/SEU_USUARIO/arcadia-suite.git
|
||||
cd arcadia-suite
|
||||
|
||||
# Instalar dependencias Node.js
|
||||
npm install
|
||||
|
||||
# Instalar dependencias Python
|
||||
python3.11 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r python-service/requirements.txt
|
||||
pip install fastapi uvicorn pandas numpy httpx pydantic nfelib lxml signxml \
|
||||
cryptography zeep beautifulsoup4 psycopg2-binary matplotlib pymongo \
|
||||
python-multipart python-docx pyopenssl
|
||||
|
||||
# Copiar arquivo de ambiente
|
||||
cp .env.example .env
|
||||
# Edite o .env com suas credenciais
|
||||
nano .env
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Banco de Dados
|
||||
|
||||
### Criar banco e usuario
|
||||
|
||||
```bash
|
||||
sudo -u postgres psql
|
||||
```
|
||||
|
||||
```sql
|
||||
-- Criar usuario
|
||||
CREATE USER arcadia_user WITH PASSWORD 'SUA_SENHA_SEGURA';
|
||||
|
||||
-- Criar banco principal
|
||||
CREATE DATABASE arcadia_suite OWNER arcadia_user;
|
||||
|
||||
-- Criar banco do Metabase (separado)
|
||||
CREATE DATABASE arcadia_metabase OWNER arcadia_user;
|
||||
|
||||
-- Permissoes
|
||||
GRANT ALL PRIVILEGES ON DATABASE arcadia_suite TO arcadia_user;
|
||||
GRANT ALL PRIVILEGES ON DATABASE arcadia_metabase TO arcadia_user;
|
||||
|
||||
\q
|
||||
```
|
||||
|
||||
### Executar migracoes (Drizzle ORM)
|
||||
|
||||
```bash
|
||||
# Sincronizar schema com o banco
|
||||
npx drizzle-kit push
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Servicos Python
|
||||
|
||||
O projeto utiliza servicos Python para funcionalidades especificas. Eles sao iniciados automaticamente pelo servidor Node.js, mas podem ser executados manualmente para testes:
|
||||
|
||||
| Servico | Porta | Funcao | Inicio Automatico |
|
||||
|---------|-------|--------|--------------------|
|
||||
| Contabil | 8003 | Motor contabil | Sim |
|
||||
| BI Engine | 8004 | Business Intelligence | Sim |
|
||||
| Automations | 8005 | Motor de automacoes | Sim |
|
||||
| Scientist | 8001 | IA, embeddings, analise de dados | Opcional* |
|
||||
| Fisco | 8002 | Motor fiscal NF-e/NFC-e | Opcional* |
|
||||
| Communication | 8006 | Motor de comunicacao | Opcional* |
|
||||
|
||||
*Servicos opcionais podem ser ativados adicionando suas chamadas em `server/index.ts`.
|
||||
|
||||
Os servicos Contabil, BI e Automations sao iniciados automaticamente pelo `server/index.ts`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Metabase (Arcadia Insights)
|
||||
|
||||
O Metabase e usado como motor de BI invisivel. Para instala-lo:
|
||||
|
||||
```bash
|
||||
# Criar diretorio
|
||||
mkdir -p metabase/plugins
|
||||
|
||||
# Baixar Metabase (versao 0.50.x recomendada)
|
||||
wget https://downloads.metabase.com/v0.50.31/metabase.jar -O metabase/metabase.jar
|
||||
|
||||
# Testar execucao
|
||||
java -jar metabase/metabase.jar &
|
||||
```
|
||||
|
||||
O Metabase sera executado na porta 8088 e proxied pelo Node.js em `/api/bi/metaset/*` e `/metabase/`.
|
||||
|
||||
O script de inicio esta em `metabase/start-metabase.sh` e e gerenciado automaticamente pelo servidor Node.js.
|
||||
|
||||
---
|
||||
|
||||
## 7. Build e Producao
|
||||
|
||||
```bash
|
||||
# Build do frontend (Vite) + backend
|
||||
npm run build
|
||||
|
||||
# Iniciar em producao
|
||||
npm run start
|
||||
```
|
||||
|
||||
O comando `npm run start` executa `NODE_ENV=production node dist/index.cjs` na porta 5000.
|
||||
|
||||
---
|
||||
|
||||
## 8. Configuracao do Servidor Web (Nginx)
|
||||
|
||||
Crie o arquivo de configuracao:
|
||||
|
||||
```bash
|
||||
sudo nano /etc/nginx/sites-available/arcadia-suite
|
||||
```
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name seu-dominio.com.br;
|
||||
|
||||
# Redirecionar HTTP para HTTPS
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name seu-dominio.com.br;
|
||||
|
||||
# SSL (sera configurado pelo Certbot)
|
||||
ssl_certificate /etc/letsencrypt/live/seu-dominio.com.br/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/seu-dominio.com.br/privkey.pem;
|
||||
|
||||
# Limites
|
||||
client_max_body_size 50M;
|
||||
|
||||
# Proxy principal (Node.js na porta 5000)
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 86400;
|
||||
}
|
||||
|
||||
# WebSocket (Socket.IO)
|
||||
location /socket.io/ {
|
||||
proxy_pass http://127.0.0.1:5000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_read_timeout 86400;
|
||||
}
|
||||
|
||||
# Arcadia Plus (Laravel, opcional)
|
||||
location /plus/ {
|
||||
proxy_pass http://127.0.0.1:8080/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
# Ativar o site
|
||||
sudo ln -s /etc/nginx/sites-available/arcadia-suite /etc/nginx/sites-enabled/
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Process Manager (PM2)
|
||||
|
||||
Use PM2 para manter o servidor rodando:
|
||||
|
||||
```bash
|
||||
# Instalar PM2 globalmente
|
||||
sudo npm install -g pm2
|
||||
|
||||
# Iniciar a aplicacao
|
||||
pm2 start dist/index.cjs --name arcadia-suite --env production
|
||||
|
||||
# Salvar a lista de processos
|
||||
pm2 save
|
||||
|
||||
# Configurar para iniciar no boot
|
||||
pm2 startup
|
||||
```
|
||||
|
||||
### Arquivo ecosystem (opcional)
|
||||
|
||||
Crie `ecosystem.config.cjs`:
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: "arcadia-suite",
|
||||
script: "dist/index.cjs",
|
||||
env: {
|
||||
NODE_ENV: "production",
|
||||
PORT: 5000,
|
||||
},
|
||||
instances: 1,
|
||||
max_memory_restart: "1G",
|
||||
log_date_format: "YYYY-MM-DD HH:mm:ss",
|
||||
error_file: "logs/error.log",
|
||||
out_file: "logs/output.log",
|
||||
},
|
||||
{
|
||||
name: "arcadia-metabase",
|
||||
script: "java",
|
||||
args: "-jar metabase/metabase.jar",
|
||||
interpreter: "none",
|
||||
env: {
|
||||
MB_DB_TYPE: "postgres",
|
||||
MB_DB_DBNAME: "arcadia_metabase",
|
||||
MB_DB_PORT: "5432",
|
||||
MB_DB_USER: "arcadia_user",
|
||||
MB_DB_PASS: "SUA_SENHA_AQUI",
|
||||
MB_DB_HOST: "localhost",
|
||||
MB_JETTY_PORT: "8088",
|
||||
},
|
||||
max_memory_restart: "2G",
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
```bash
|
||||
# Iniciar com ecosystem
|
||||
pm2 start ecosystem.config.cjs
|
||||
pm2 save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. SSL/HTTPS
|
||||
|
||||
```bash
|
||||
# Certificado gratuito com Let's Encrypt
|
||||
sudo certbot --nginx -d seu-dominio.com.br
|
||||
|
||||
# Renovacao automatica (ja configurada pelo Certbot)
|
||||
sudo certbot renew --dry-run
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Estrutura de Portas
|
||||
|
||||
| Porta | Servico | Acesso |
|
||||
|-------|---------|--------|
|
||||
| 5000 | Node.js (Express + Vite) | Principal - proxied pelo Nginx |
|
||||
| 8088 | Metabase (Arcadia Insights) | Interno - proxied via /api/bi/metaset e /metabase |
|
||||
| 8003 | Python Contabil | Interno - inicio automatico |
|
||||
| 8004 | Python BI Engine | Interno - inicio automatico |
|
||||
| 8005 | Python Automations | Interno - inicio automatico |
|
||||
| 8001 | Python Scientist | Interno - opcional |
|
||||
| 8002 | Python Fisco | Interno - opcional |
|
||||
| 8006 | Node Communication | Interno - opcional |
|
||||
| 8080 | Arcadia Plus (Laravel) | Interno - proxied via /plus |
|
||||
|
||||
Apenas a porta 80 (HTTP) e 443 (HTTPS) devem estar expostas publicamente via Nginx.
|
||||
|
||||
---
|
||||
|
||||
## 12. Arcadia Plus (Laravel)
|
||||
|
||||
Se voce utiliza o modulo Plus (ERP Laravel):
|
||||
|
||||
```bash
|
||||
cd plus
|
||||
|
||||
# Instalar dependencias PHP
|
||||
composer install --no-dev --optimize-autoloader
|
||||
|
||||
# Configurar ambiente
|
||||
cp .env.example .env
|
||||
php artisan key:generate
|
||||
|
||||
# Configurar banco (usar schema separado no mesmo PostgreSQL)
|
||||
# Edite plus/.env com as credenciais do banco
|
||||
|
||||
# Migracoes
|
||||
php artisan migrate
|
||||
|
||||
# Otimizar para producao
|
||||
php artisan config:cache
|
||||
php artisan route:cache
|
||||
php artisan view:cache
|
||||
|
||||
# Servir (porta 8080)
|
||||
php artisan serve --host=0.0.0.0 --port=8080
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. Manutencao e Backups
|
||||
|
||||
### Backup do Banco de Dados
|
||||
|
||||
```bash
|
||||
# Criar backup
|
||||
pg_dump -U arcadia_user -h localhost arcadia_suite > backup_$(date +%Y%m%d_%H%M%S).sql
|
||||
|
||||
# Restaurar backup
|
||||
psql -U arcadia_user -h localhost arcadia_suite < backup_20260209.sql
|
||||
```
|
||||
|
||||
### Atualizar a Aplicacao
|
||||
|
||||
```bash
|
||||
cd /caminho/para/arcadia-suite
|
||||
|
||||
# Parar a aplicacao
|
||||
pm2 stop arcadia-suite
|
||||
|
||||
# Atualizar codigo
|
||||
git pull origin main
|
||||
|
||||
# Reinstalar dependencias
|
||||
npm install
|
||||
|
||||
# Rebuild
|
||||
npm run build
|
||||
|
||||
# Migrar banco (se necessario)
|
||||
npx drizzle-kit push
|
||||
|
||||
# Reiniciar
|
||||
pm2 restart arcadia-suite
|
||||
```
|
||||
|
||||
### Logs
|
||||
|
||||
```bash
|
||||
# Ver logs em tempo real
|
||||
pm2 logs arcadia-suite
|
||||
|
||||
# Ver logs do Nginx
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. Troubleshooting
|
||||
|
||||
### Erro: "Cannot find module"
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Erro: "ECONNREFUSED" no banco
|
||||
```bash
|
||||
# Verificar se PostgreSQL esta rodando
|
||||
sudo systemctl status postgresql
|
||||
sudo systemctl start postgresql
|
||||
```
|
||||
|
||||
### Erro: "Port already in use"
|
||||
```bash
|
||||
# Encontrar processo na porta
|
||||
lsof -i :5000
|
||||
kill -9 PID_DO_PROCESSO
|
||||
```
|
||||
|
||||
### Servicos Python nao iniciam
|
||||
```bash
|
||||
# Verificar Python
|
||||
python3.11 --version
|
||||
|
||||
# Verificar ambiente virtual
|
||||
source .venv/bin/activate
|
||||
pip list | grep fastapi
|
||||
```
|
||||
|
||||
### Metabase nao inicia
|
||||
```bash
|
||||
# Verificar Java
|
||||
java -version
|
||||
|
||||
# Verificar se metabase.jar existe
|
||||
ls -la metabase/metabase.jar
|
||||
|
||||
# Executar manualmente para ver erros
|
||||
java -jar metabase/metabase.jar
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hospedagem em Cloud (Alternativas)
|
||||
|
||||
### VPS Recomendados
|
||||
- **Hetzner:** A partir de EUR 4/mes (melhor custo-beneficio)
|
||||
- **DigitalOcean:** A partir de USD 12/mes (4GB RAM)
|
||||
- **Vultr:** A partir de USD 12/mes
|
||||
- **AWS EC2:** t3.medium (sob demanda)
|
||||
- **Contabo:** A partir de EUR 5/mes (alto recurso por preco)
|
||||
|
||||
### Deploy com Docker (Alternativa)
|
||||
|
||||
Se preferir usar Docker, crie um `Dockerfile`:
|
||||
|
||||
```dockerfile
|
||||
FROM node:20-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Instalar Python e Java
|
||||
RUN apt-get update && apt-get install -y \
|
||||
python3.11 python3.11-venv python3-pip \
|
||||
openjdk-11-jre-headless \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copiar e instalar dependencias
|
||||
COPY package.json ./
|
||||
RUN npm install --production
|
||||
|
||||
# Instalar dependencias Python
|
||||
COPY python-service/requirements.txt ./python-requirements.txt
|
||||
RUN python3.11 -m venv /app/.venv && \
|
||||
/app/.venv/bin/pip install -r python-requirements.txt
|
||||
|
||||
# Copiar codigo
|
||||
COPY . .
|
||||
|
||||
# Build
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
CMD ["npm", "run", "start"]
|
||||
```
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
version: "3.8"
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
ports:
|
||||
- "5000:5000"
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://arcadia_user:senha@db:5432/arcadia_suite
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
db:
|
||||
image: postgres:15
|
||||
environment:
|
||||
POSTGRES_USER: arcadia_user
|
||||
POSTGRES_PASSWORD: senha
|
||||
POSTGRES_DB: arcadia_suite
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
|
||||
metabase:
|
||||
image: metabase/metabase:v0.50.31
|
||||
ports:
|
||||
- "8088:8088"
|
||||
environment:
|
||||
MB_DB_TYPE: postgres
|
||||
MB_DB_DBNAME: arcadia_metabase
|
||||
MB_DB_PORT: 5432
|
||||
MB_DB_USER: arcadia_user
|
||||
MB_DB_PASS: senha
|
||||
MB_DB_HOST: db
|
||||
MB_JETTY_PORT: 8088
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checklist de Deploy
|
||||
|
||||
- [ ] Servidor provisionado com requisitos minimos
|
||||
- [ ] PostgreSQL instalado e configurado
|
||||
- [ ] Node.js 20 instalado
|
||||
- [ ] Python 3.11 instalado
|
||||
- [ ] Repositorio clonado
|
||||
- [ ] Dependencias instaladas (npm install)
|
||||
- [ ] Dependencias Python instaladas
|
||||
- [ ] Arquivo .env configurado com todas as variaveis
|
||||
- [ ] Banco de dados criado e migracoes executadas
|
||||
- [ ] Build de producao executado (npm run build)
|
||||
- [ ] Metabase baixado e configurado
|
||||
- [ ] PM2 configurado e aplicacao rodando
|
||||
- [ ] Nginx configurado como reverse proxy
|
||||
- [ ] SSL/HTTPS configurado com Certbot
|
||||
- [ ] Firewall configurado (portas 80 e 443 abertas)
|
||||
- [ ] Backup automatico do banco configurado
|
||||
- [ ] Dominio apontando para o servidor
|
||||
|
||||
---
|
||||
|
||||
*Documento gerado em 09/02/2026 - Arcadia Suite v1.0*
|
||||
|
|
@ -0,0 +1,885 @@
|
|||
# Arcádia Automações — Mapa Completo do Motor de Automação
|
||||
|
||||
> Mapa da arquitetura de automações da Arcádia Suite: Automation Engine
|
||||
> (FastAPI :8005), Scheduler (Cron), Event Bus (Pub/Sub), Workflow
|
||||
> Executor (multi-step), Service Node.js (fallback), XOS Automations
|
||||
> (CRM), BPMN Viewer, e integração com Manus IA.
|
||||
> Atualizado em: Março 2026
|
||||
|
||||
---
|
||||
|
||||
## 1. Visão Geral
|
||||
|
||||
O sistema de automações opera em **duas camadas** coordenadas: um motor Python (FastAPI) que executa workflows complexos com scheduler e event bus, e um serviço Node.js que gerencia o CRUD das automações e funciona como fallback de agendamento.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ARCÁDIA AUTOMAÇÕES — STACK COMPLETO │
|
||||
│ │
|
||||
│ ┌─────────── Frontend ──────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ Automations.tsx XosAutomations.tsx WorkflowBuilder │ │
|
||||
│ │ (559 linhas) (350 linhas) (493 linhas) │ │
|
||||
│ │ CRUD + Trigger CRM workflows Visual editor │ │
|
||||
│ │ │ │
|
||||
│ │ BpmnPage.tsx BpmnDiagram.tsx ProcessDiagram │ │
|
||||
│ │ (12 linhas) (645 linhas) (184 linhas) │ │
|
||||
│ │ BPMN viewer Diagrama visual Fluxos visuais │ │
|
||||
│ └────────────────────────┬───────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────── Gateway (Node.js :5000) ───────────────────────┐ │
|
||||
│ │ │ │ │
|
||||
│ │ /api/automations/* ←───┤ CRUD + Execução (Drizzle) │ │
|
||||
│ │ (routes.ts — 343L) │ │ │
|
||||
│ │ │ │ │
|
||||
│ │ /api/automation-engine/* ←┤ Proxy → FastAPI :8005 │ │
|
||||
│ │ (engine-proxy.ts — 267L) │ │ │
|
||||
│ │ │ │ │
|
||||
│ │ AutomationService ←┤ Executor + Scheduler Node │ │
|
||||
│ │ (service.ts — 209L) │ (polling 60s fallback) │ │
|
||||
│ └────────────────────────┬───────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────── Motor Python (FastAPI :8005) ──────────────────┐ │
|
||||
│ │ │ │ │
|
||||
│ │ automation_engine.py (668 linhas) │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │
|
||||
│ │ │ Scheduler│ │Event Bus │ │Workflow Executor │ │ │
|
||||
│ │ │ (Cron) │ │(Pub/Sub) │ │(Multi-step) │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ │ │ Entries │ │ Subscribe│ │ Condition │ │ │
|
||||
│ │ │ Start/ │ │ Emit │ │ Action │ │ │
|
||||
│ │ │ Stop │ │ History │ │ Delay │ │ │
|
||||
│ │ │ 30s loop │ │ 500 max │ │ SQL Query │ │ │
|
||||
│ │ │ │ │ │ │ HTTP Request │ │ │
|
||||
│ │ │ │ │ │ │ Transform │ │ │
|
||||
│ │ │ │ │ │ │ Notify │ │ │
|
||||
│ │ └──────────┘ └──────────┘ └──────────────────┘ │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── PostgreSQL ────────────────────────────────────┐ │
|
||||
│ │ automations, automation_actions, automation_logs, │ │
|
||||
│ │ scheduled_tasks, xos_automations │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Automation Engine (Python FastAPI — :8005)
|
||||
|
||||
O motor Python é o coração do sistema de automações. Roda como processo separado e expõe 3 subsistemas via REST:
|
||||
|
||||
### 2.1 — Event Bus (Barramento de Eventos)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ EventBus — Pub/Sub em memória │
|
||||
│ server/python/automation_engine.py:117 │
|
||||
│ │
|
||||
│ ┌─── Tipos de Eventos (EventType) ──────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ record.created → Registro criado no banco │ │
|
||||
│ │ record.updated → Registro atualizado │ │
|
||||
│ │ record.deleted → Registro deletado │ │
|
||||
│ │ schedule.fired → Scheduler disparou cron │ │
|
||||
│ │ webhook.received → Webhook externo recebido │ │
|
||||
│ │ threshold.reached → Limiar atingido │ │
|
||||
│ │ agent.completed → Manus IA finalizou tarefa │ │
|
||||
│ │ manual.trigger → Disparo manual do usuário │ │
|
||||
│ │ system.event → Evento interno do sistema │ │
|
||||
│ │ │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── Operações ─────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ subscribe(event_type, handler_id, config) │ │
|
||||
│ │ → Registra um handler para um tipo de evento │ │
|
||||
│ │ │ │
|
||||
│ │ unsubscribe(event_type, handler_id) │ │
|
||||
│ │ → Remove assinatura │ │
|
||||
│ │ │ │
|
||||
│ │ emit(event_type, payload) │ │
|
||||
│ │ → Emite evento, retorna handlers triggerados │ │
|
||||
│ │ → Suporta wildcard "*" (ouve tudo) │ │
|
||||
│ │ │ │
|
||||
│ │ get_history(limit, event_type) │ │
|
||||
│ │ → Últimos eventos (máx. 500 em memória) │ │
|
||||
│ │ │ │
|
||||
│ │ stats() │ │
|
||||
│ │ → total_event_types, total_subscribers, │ │
|
||||
│ │ history_size, event_types list │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Fluxo: │
|
||||
│ │
|
||||
│ Emissor ──emit("record.created", {entity: "lead"})──▶ EventBus │
|
||||
│ │ │
|
||||
│ ┌────────────────────────────────┤ │
|
||||
│ ▼ ▼ │
|
||||
│ Handler A Handler B │
|
||||
│ (workflow X) (notificação) │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 — Scheduler (Agendador Cron)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ Scheduler — Cron-based scheduler │
|
||||
│ server/python/automation_engine.py:186 │
|
||||
│ │
|
||||
│ ┌─── CronExpression Parser ─────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ Formato: "minuto hora dia mês dia_semana" │ │
|
||||
│ │ Suporta: *, ranges (1-5), steps (*/5), lists (1,3,5) │ │
|
||||
│ │ │ │
|
||||
│ │ Exemplos: │ │
|
||||
│ │ "0 9 * * 1-5" → Seg-Sex às 9h │ │
|
||||
│ │ "*/15 * * * *" → A cada 15 minutos │ │
|
||||
│ │ "0 0 1 * *" → 1º dia de cada mês à meia-noite │ │
|
||||
│ │ │ │
|
||||
│ │ matches(datetime) → bool │ │
|
||||
│ │ next_run(from_dt) → datetime │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── SchedulerEntry ────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ id: Identificador único │ │
|
||||
│ │ name: Nome descritivo │ │
|
||||
│ │ cron: Expressão cron (5 partes) │ │
|
||||
│ │ automation_id: Link para automação (opcional) │ │
|
||||
│ │ action: "trigger" (padrão) │ │
|
||||
│ │ config: Configurações extras (JSON) │ │
|
||||
│ │ is_active: Ativo/Inativo │ │
|
||||
│ │ last_run: Última execução (ISO) │ │
|
||||
│ │ next_run: Próxima execução (ISO) │ │
|
||||
│ │ run_count: Contador de execuções │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── Loop de Execução ──────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ Thread daemon, check_interval = 30 segundos │ │
|
||||
│ │ │ │
|
||||
│ │ A cada 30s: │ │
|
||||
│ │ 1. Varre todas as entries ativas │ │
|
||||
│ │ 2. Para cada entry: cron.matches(agora)? │ │
|
||||
│ │ 3. Se match: │ │
|
||||
│ │ a. Atualiza last_run, run_count │ │
|
||||
│ │ b. Calcula next_run │ │
|
||||
│ │ c. event_bus.emit("schedule.fired", { │ │
|
||||
│ │ scheduler_id, automation_id, name │ │
|
||||
│ │ }) │ │
|
||||
│ │ 4. Sleep(30s) │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Inicia automaticamente no startup do FastAPI │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.3 — Workflow Executor (Executor Multi-Step)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ WorkflowExecutor │
|
||||
│ server/python/automation_engine.py:273 │
|
||||
│ │
|
||||
│ ┌─── WorkflowDefinition ────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ id: Identificador do workflow │ │
|
||||
│ │ name: Nome descritivo │ │
|
||||
│ │ steps: Lista de WorkflowStep │ │
|
||||
│ │ trigger: Tipo de trigger (opcional) │ │
|
||||
│ │ variables: Variáveis iniciais (Dict) │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── WorkflowStep ─────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ id: Identificador do step │ │
|
||||
│ │ type: Tipo (ver abaixo) │ │
|
||||
│ │ config: Configuração do step (Dict) │ │
|
||||
│ │ on_success: Próximo step se OK (opcional) │ │
|
||||
│ │ on_failure: Próximo step se falha (opcional) │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── Tipos de Steps (WorkflowStepType) ─────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ condition → Avalia condição (field, operator, value)│ │
|
||||
│ │ Operadores: ==, !=, >, <, >=, <=, │ │
|
||||
│ │ contains, exists │ │
|
||||
│ │ │ │
|
||||
│ │ action → Executa ação: │ │
|
||||
│ │ • log (registra mensagem) │ │
|
||||
│ │ • set_variable (define variável) │ │
|
||||
│ │ • emit_event (emite evento no bus) │ │
|
||||
│ │ │ │
|
||||
│ │ delay → Pausa execução (max 30s) │ │
|
||||
│ │ │ │
|
||||
│ │ query → Executa SELECT no PostgreSQL │ │
|
||||
│ │ Conexão read-only, timeout 10s, │ │
|
||||
│ │ limite 100 rows │ │
|
||||
│ │ │ │
|
||||
│ │ http → Requisição HTTP (GET/POST/PUT/DELETE) │ │
|
||||
│ │ Timeout 10s, resposta truncada em 5KB │ │
|
||||
│ │ │ │
|
||||
│ │ transform → Transforma dados: │ │
|
||||
│ │ • count (conta itens) │ │
|
||||
│ │ • sum (soma campo numérico) │ │
|
||||
│ │ • filter (filtra por campo/valor) │ │
|
||||
│ │ │ │
|
||||
│ │ notify → Envia notificação (message + channel) │ │
|
||||
│ │ │ │
|
||||
│ │ loop → Iteração (planejado) │ │
|
||||
│ │ parallel → Execução paralela (planejado) │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── Fluxo de Execução ────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ 1. Recebe workflow_id + trigger_data + variables │ │
|
||||
│ │ 2. Cria execution com status "running" │ │
|
||||
│ │ 3. Merge: workflow.variables + variables + trigger │ │
|
||||
│ │ 4. Para cada step (sequencial): │ │
|
||||
│ │ a. Executa _execute_step(step, variables) │ │
|
||||
│ │ b. Se resultado tem "output" → merge em variables │ │
|
||||
│ │ c. Registra resultado do step │ │
|
||||
│ │ 5. Status final: "completed" ou "error" │ │
|
||||
│ │ 6. Guarda em memória (máx. 200 execuções) │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Segurança: │
|
||||
│ • SQL: somente SELECT, read-only connection, timeout 10s │
|
||||
│ • HTTP: timeout 10s, resposta truncada │
|
||||
│ • Delay: máximo 30 segundos │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Automation Service (Node.js — Fallback)
|
||||
|
||||
O serviço Node.js complementa o motor Python, fornecendo CRUD das automações e um scheduler de fallback:
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ AutomationService │
|
||||
│ server/automations/service.ts (209 linhas) │
|
||||
│ │
|
||||
│ ┌─── Executor de Ações (Node.js) ──────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ runAutomation(automationId, userId, triggerData) │ │
|
||||
│ │ │ │
|
||||
│ │ 1. Cria log com status "running" │ │
|
||||
│ │ 2. Busca automation + actions (orderBy orderIndex) │ │
|
||||
│ │ 3. Executa cada action sequencialmente: │ │
|
||||
│ │ │ │
|
||||
│ │ Tipos de ação suportados: │ │
|
||||
│ │ │ │
|
||||
│ │ ┌────────────────────────────────────────────┐ │ │
|
||||
│ │ │ agent_task → Dispara Manus IA │ │ │
|
||||
│ │ │ manusService.run(prompt) │ │ │
|
||||
│ │ │ Aguarda até 120s │ │ │
|
||||
│ │ │ Polls a cada 2s │ │ │
|
||||
│ │ ├────────────────────────────────────────────┤ │ │
|
||||
│ │ │ send_notification→ Registra notificação │ │ │
|
||||
│ │ ├────────────────────────────────────────────┤ │ │
|
||||
│ │ │ erp_sync → Inicia sync com ERP │ │ │
|
||||
│ │ ├────────────────────────────────────────────┤ │ │
|
||||
│ │ │ generate_report → Gera relatório │ │ │
|
||||
│ │ ├────────────────────────────────────────────┤ │ │
|
||||
│ │ │ webhook → Chama URL externa │ │ │
|
||||
│ │ │ POST + JSON payload │ │ │
|
||||
│ │ ├────────────────────────────────────────────┤ │ │
|
||||
│ │ │ send_email → Envia email │ │ │
|
||||
│ │ ├────────────────────────────────────────────┤ │ │
|
||||
│ │ │ send_whatsapp → Envia msg WhatsApp │ │ │
|
||||
│ │ └────────────────────────────────────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ 4. Atualiza log: "completed" ou "error" │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── Scheduler (Fallback) ──────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ startScheduler() │ │
|
||||
│ │ → setInterval(60000) — verifica a cada 60s │ │
|
||||
│ │ │ │
|
||||
│ │ checkScheduledTasks() │ │
|
||||
│ │ → JOIN scheduled_tasks + automations │ │
|
||||
│ │ → WHERE isActive = true AND nextRunAt <= agora │ │
|
||||
│ │ → Executa runAutomation() para cada task due │ │
|
||||
│ │ → Calcula nextRunAt baseado em intervalMinutes │ │
|
||||
│ │ → Atualiza lastRunAt │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Integração com Manus IA: │
|
||||
│ • action "agent_task" → manusService.run(userId, prompt) │
|
||||
│ • Aguarda execução com polling (até 120s) │
|
||||
│ • Verifica status: completed | stopped | error │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Engine Proxy (Node.js → Python)
|
||||
|
||||
O proxy traduz chamadas `/api/automation-engine/*` para o FastAPI em `:8005`:
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ Engine Proxy │
|
||||
│ server/automations/engine-proxy.ts (267 linhas) │
|
||||
│ │
|
||||
│ Config: │
|
||||
│ AUTOMATION_ENGINE_HOST = localhost (ou env var) │
|
||||
│ AUTOMATION_ENGINE_PORT = 8005 │
|
||||
│ TIMEOUT = 30 segundos │
|
||||
│ │
|
||||
│ Tradução: │
|
||||
│ │
|
||||
│ /api/automation-engine/health → GET :8005/health │
|
||||
│ /api/automation-engine/version → GET :8005/version │
|
||||
│ /api/automation-engine/metrics → GET :8005/metrics │
|
||||
│ │
|
||||
│ /api/automation-engine/scheduler/ → :8005/scheduler/ │
|
||||
│ entries (GET/POST/DELETE) │
|
||||
│ start (POST) │
|
||||
│ stop (POST) │
|
||||
│ │
|
||||
│ /api/automation-engine/events/ → :8005/events/ │
|
||||
│ emit (POST) │
|
||||
│ subscribe (POST) │
|
||||
│ subscribers (GET) │
|
||||
│ history (GET) │
|
||||
│ types (GET) │
|
||||
│ │
|
||||
│ /api/automation-engine/workflows/ → :8005/workflows/ │
|
||||
│ register (POST) │
|
||||
│ list (GET) │
|
||||
│ get/:id (GET) │
|
||||
│ execute/:id (POST) │
|
||||
│ executions/:id (GET) │
|
||||
│ │
|
||||
│ /api/automation-engine/executions → :8005/executions │
|
||||
│ /api/automation-engine/cron/validate → :8005/cron/validate │
|
||||
│ │
|
||||
│ Se engine offline → retorna { status: "offline" } no /health │
|
||||
│ Demais rotas → 502 Bad Gateway se timeout │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. XOS Automations (CRM Workflows)
|
||||
|
||||
Automações específicas do CRM com triggers baseados em eventos de negócio:
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ XOS Automations │
|
||||
│ Tabela: xos_automations — shared/schema.ts:6593 │
|
||||
│ UI: client/src/pages/XosAutomations.tsx (350 linhas) │
|
||||
│ │
|
||||
│ ┌─── Schema ────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ id serial PK │ │
|
||||
│ │ tenantId FK → tenants.id │ │
|
||||
│ │ name varchar(200) NOT NULL │ │
|
||||
│ │ description text │ │
|
||||
│ │ triggerType varchar(50) NOT NULL │ │
|
||||
│ │ triggerConfig JSONB { condições do trigger } │ │
|
||||
│ │ actions JSONB [ { type, config } ] │ │
|
||||
│ │ conditions JSONB [ { field, operator, value } ] │ │
|
||||
│ │ isActive boolean (default true) │ │
|
||||
│ │ executionCount integer (default 0) │ │
|
||||
│ │ lastExecutedAt timestamp │ │
|
||||
│ │ createdBy FK → users.id │ │
|
||||
│ │ createdAt, updatedAt timestamp │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── Triggers CRM ─────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ contact_created → Novo contato cadastrado │ │
|
||||
│ │ deal_stage_changed → Deal mudou de estágio │ │
|
||||
│ │ form_submitted → Formulário preenchido │ │
|
||||
│ │ ticket_created → Ticket de suporte aberto │ │
|
||||
│ │ deal_won → Negócio fechado (ganho) │ │
|
||||
│ │ deal_lost → Negócio perdido │ │
|
||||
│ │ message_received → Mensagem WhatsApp recebida │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── Ações CRM ────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ send_email → Email automático │ │
|
||||
│ │ send_whatsapp → Mensagem WhatsApp │ │
|
||||
│ │ create_task → Cria tarefa/atividade │ │
|
||||
│ │ assign_agent → Atribui agente ao ticket │ │
|
||||
│ │ update_field → Atualiza campo do registro │ │
|
||||
│ │ move_deal_stage → Move deal no pipeline │ │
|
||||
│ │ webhook → Chama URL externa │ │
|
||||
│ │ notify_team → Notifica equipe │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Diferença vs automations genérica: │
|
||||
│ • xos_automations → multi-tenant (tenantId) │
|
||||
│ • xos_automations → triggers de negócio (CRM) │
|
||||
│ • xos_automations → conditions inline (JSONB array) │
|
||||
│ • automations → por usuário (userId) │
|
||||
│ • automations → triggers genéricos (schedule/webhook/manual)│
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Banco de Dados — Tabelas
|
||||
|
||||
### 6.1 — automations (Automações Genéricas)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ automations — shared/schema.ts:582 │
|
||||
│ │
|
||||
│ id serial PK │
|
||||
│ userId FK → users.id (CASCADE) │
|
||||
│ name text NOT NULL │
|
||||
│ description text │
|
||||
│ triggerType text NOT NULL │
|
||||
│ "schedule" | "webhook" | "manual" | "event" │
|
||||
│ triggerConfig text (JSON stringified) │
|
||||
│ isActive "true" | "false" │
|
||||
│ createdAt timestamp │
|
||||
│ updatedAt timestamp │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 6.2 — automation_actions (Ações da Automação)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ automation_actions — shared/schema.ts:594 │
|
||||
│ │
|
||||
│ id serial PK │
|
||||
│ automationId FK → automations.id (CASCADE) │
|
||||
│ orderIndex integer (ordem de execução) │
|
||||
│ actionType text NOT NULL │
|
||||
│ "agent_task" | "send_notification" | │
|
||||
│ "erp_sync" | "generate_report" | │
|
||||
│ "webhook" | "send_email" | "send_whatsapp" │
|
||||
│ actionConfig text (JSON: url, method, to, prompt, etc.) │
|
||||
│ conditionConfig text (JSON: condição para executar) │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 6.3 — automation_logs (Histórico de Execuções)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ automation_logs — shared/schema.ts:603 │
|
||||
│ │
|
||||
│ id serial PK │
|
||||
│ automationId FK → automations.id (CASCADE) │
|
||||
│ status "running" | "completed" | "error" │
|
||||
│ triggerData text (JSON: dados que dispararam) │
|
||||
│ result text (resultado da execução) │
|
||||
│ error text (mensagem de erro, se falhou) │
|
||||
│ startedAt timestamp │
|
||||
│ completedAt timestamp │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 6.4 — scheduled_tasks (Tarefas Agendadas)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ scheduled_tasks — shared/schema.ts:614 │
|
||||
│ │
|
||||
│ id serial PK │
|
||||
│ automationId FK → automations.id (CASCADE) │
|
||||
│ cronExpression text ("0 9 * * 1-5") │
|
||||
│ intervalMinutes integer (alternativa ao cron) │
|
||||
│ nextRunAt timestamp (próxima execução) │
|
||||
│ lastRunAt timestamp (última execução) │
|
||||
│ isActive "true" | "false" │
|
||||
│ │
|
||||
│ Dois modos: │
|
||||
│ 1. cronExpression → Parser Python (5 partes) │
|
||||
│ 2. intervalMinutes → Simples: nextRunAt += interval │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 6.5 — xos_automations (CRM Automations)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ xos_automations — shared/schema.ts:6593 │
|
||||
│ │
|
||||
│ id serial PK │
|
||||
│ tenantId FK → tenants.id (CASCADE) │
|
||||
│ name varchar(200) NOT NULL │
|
||||
│ description text │
|
||||
│ triggerType varchar(50) NOT NULL │
|
||||
│ triggerConfig JSONB │
|
||||
│ actions JSONB [ { type, config } ] │
|
||||
│ conditions JSONB [ { field, operator, value } ] │
|
||||
│ isActive boolean (default true) │
|
||||
│ executionCount integer (default 0) │
|
||||
│ lastExecutedAt timestamp │
|
||||
│ createdBy FK → users.id │
|
||||
│ createdAt, updatedAt timestamp │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. API REST Completa
|
||||
|
||||
### 7.1 — CRUD Automações (/api/automations/*)
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/automations` | Lista automações do usuário |
|
||||
| GET | `/api/automations/:id` | Detalhe (com actions + schedule) |
|
||||
| POST | `/api/automations` | Cria automação (+ actions + schedule) |
|
||||
| PUT | `/api/automations/:id` | Atualiza automação completa |
|
||||
| DELETE | `/api/automations/:id` | Exclui automação (cascade) |
|
||||
| POST | `/api/automations/:id/run` | Executa manualmente |
|
||||
| GET | `/api/automations/:id/logs` | Histórico de execuções (últimas 50) |
|
||||
|
||||
### 7.2 — Automation Engine (/api/automation-engine/*)
|
||||
|
||||
#### Saúde e Métricas
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/health` | Status (online/offline + stats) |
|
||||
| GET | `/version` | Versão + capabilities |
|
||||
| GET | `/metrics` | Métricas: scheduler, event_bus, workflows |
|
||||
|
||||
#### Scheduler
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/scheduler/entries` | Lista entradas cron |
|
||||
| POST | `/scheduler/entries` | Adiciona entrada cron |
|
||||
| DELETE | `/scheduler/entries/:id` | Remove entrada |
|
||||
| POST | `/scheduler/start` | Inicia loop do scheduler |
|
||||
| POST | `/scheduler/stop` | Para loop do scheduler |
|
||||
|
||||
#### Event Bus
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| POST | `/events/emit` | Emite evento |
|
||||
| POST | `/events/subscribe` | Registra handler |
|
||||
| GET | `/events/subscribers` | Lista assinantes |
|
||||
| GET | `/events/history` | Histórico de eventos |
|
||||
| GET | `/events/types` | Tipos de eventos disponíveis |
|
||||
|
||||
#### Workflows
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| POST | `/workflows/register` | Registra workflow (steps) |
|
||||
| GET | `/workflows` | Lista workflows |
|
||||
| GET | `/workflows/:id` | Detalhe do workflow |
|
||||
| POST | `/workflows/:id/execute` | Executa workflow |
|
||||
| GET | `/workflows/:id/executions` | Execuções do workflow |
|
||||
| GET | `/executions` | Todas as execuções |
|
||||
|
||||
#### Cron
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| POST | `/cron/validate` | Valida expressão cron + próximas 5 execuções |
|
||||
|
||||
**Total: 27+ endpoints de automação**
|
||||
|
||||
---
|
||||
|
||||
## 8. Frontend — Interfaces
|
||||
|
||||
### 8.1 — Automations.tsx (Gestão Geral)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ Automations.tsx — 559 linhas │
|
||||
│ Rota: /automations │
|
||||
│ │
|
||||
│ Componentes: │
|
||||
│ │
|
||||
│ ┌── CreateAutomationDialog ─────────────────────────────┐ │
|
||||
│ │ • Nome, descrição │ │
|
||||
│ │ • Tipo de trigger: schedule | webhook | manual | event│ │
|
||||
│ │ • Config do trigger (JSON) │ │
|
||||
│ │ • Actions: lista de { actionType, actionConfig } │ │
|
||||
│ │ • Schedule: cronExpression ou intervalMinutes │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── AutomationCard ────────────────────────────────────┐ │
|
||||
│ │ • Nome, tipo de trigger, status (ativo/inativo) │ │
|
||||
│ │ • Botão: Executar agora │ │
|
||||
│ │ • Botão: Ativar/Desativar │ │
|
||||
│ │ • Botão: Excluir │ │
|
||||
│ │ • Logs de execução (últimas 50) │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Lista todas as automações do usuário logado │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 8.2 — XosAutomations.tsx (CRM Workflows)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ XosAutomations.tsx — 350 linhas │
|
||||
│ Rota: /xos/automations │
|
||||
│ │
|
||||
│ Foco em automações de CRM: │
|
||||
│ • Triggers de negócio (contact_created, deal_stage_changed) │
|
||||
│ • Ações comerciais (send_email, assign_agent, move_deal) │
|
||||
│ • Condições inline (campo, operador, valor) │
|
||||
│ • Contagem de execuções e última execução │
|
||||
│ • Vinculado ao tenant (multi-tenant) │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 8.3 — WorkflowBuilder.tsx (Editor Visual)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ WorkflowBuilder.tsx — 493 linhas │
|
||||
│ Rota: /workflow-builder │
|
||||
│ │
|
||||
│ Editor visual de workflows: │
|
||||
│ • Drag & drop de steps │
|
||||
│ • Configuração de cada step (type + config) │
|
||||
│ • Visualização do fluxo │
|
||||
│ • Registro no Workflow Executor do engine │
|
||||
│ • Execução e monitoramento │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 8.4 — BPMN (Diagramas de Processo)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ BpmnDiagram.tsx — 645 linhas │
|
||||
│ ProcessDiagram.tsx — 184 linhas │
|
||||
│ BpmnPage.tsx — 12 linhas (wrapper) │
|
||||
│ │
|
||||
│ Visualização BPMN de processos: │
|
||||
│ • Render de diagramas BPMN 2.0 │
|
||||
│ • Nós: start, end, task, gateway, event │
|
||||
│ • Conexões direcionais │
|
||||
│ • Zoom e pan │
|
||||
│ • Usado no Process Compass (Módulo Consultivo) │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Fluxo de Execução Completo
|
||||
|
||||
### 9.1 — Automação Manual (Trigger: manual)
|
||||
|
||||
```
|
||||
Usuário clica "Executar" na UI
|
||||
│
|
||||
▼
|
||||
POST /api/automations/:id/run
|
||||
│
|
||||
▼
|
||||
AutomationService.runAutomation()
|
||||
│
|
||||
├──▶ INSERT automation_logs (status: "running")
|
||||
│
|
||||
├──▶ SELECT automation_actions ORDER BY orderIndex
|
||||
│
|
||||
├──▶ Para cada action:
|
||||
│ ├── agent_task → manusService.run() → GPT-4o
|
||||
│ ├── webhook → fetch(url, POST, body)
|
||||
│ ├── send_email → (placeholder)
|
||||
│ ├── send_whatsapp → (placeholder)
|
||||
│ ├── erp_sync → (placeholder)
|
||||
│ └── generate_report → (placeholder)
|
||||
│
|
||||
└──▶ UPDATE automation_logs (status: "completed")
|
||||
```
|
||||
|
||||
### 9.2 — Automação Agendada (Trigger: schedule)
|
||||
|
||||
```
|
||||
┌───────────────────── Scheduler Python ──────────────────────┐
|
||||
│ │
|
||||
│ Thread daemon (30s loop) │
|
||||
│ ↓ │
|
||||
│ CronExpression.matches(agora)? → SIM │
|
||||
│ ↓ │
|
||||
│ event_bus.emit("schedule.fired", {automation_id: 42}) │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼ (subscriber triggerado)
|
||||
Handler executa ação configurada
|
||||
|
||||
┌───────────── OU ── Scheduler Node.js (Fallback) ────────────┐
|
||||
│ │
|
||||
│ setInterval(60000) → checkScheduledTasks() │
|
||||
│ ↓ │
|
||||
│ SELECT scheduled_tasks │
|
||||
│ JOIN automations │
|
||||
│ WHERE isActive AND nextRunAt <= NOW() │
|
||||
│ ↓ │
|
||||
│ AutomationService.runAutomation(id, userId) │
|
||||
│ ↓ │
|
||||
│ UPDATE nextRunAt = NOW() + intervalMinutes │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 9.3 — Workflow Engine (Multi-Step)
|
||||
|
||||
```
|
||||
POST /api/automation-engine/workflows/register
|
||||
│
|
||||
▼ Registra WorkflowDefinition com N steps
|
||||
|
||||
POST /api/automation-engine/workflows/:id/execute
|
||||
│
|
||||
▼
|
||||
WorkflowExecutor.execute(workflow_id, trigger_data, variables)
|
||||
│
|
||||
▼
|
||||
┌────────────── EXECUÇÃO SEQUENCIAL ──────────────────────────┐
|
||||
│ │
|
||||
│ Step 1: condition │
|
||||
│ ├── field: "revenue" operator: ">" value: 10000 │
|
||||
│ └── result: true → continua │
|
||||
│ │
|
||||
│ Step 2: query │
|
||||
│ ├── sql: "SELECT COUNT(*) FROM crm_leads WHERE ..." │
|
||||
│ └── output: { query_result: [{count: 42}] } │
|
||||
│ │
|
||||
│ Step 3: http │
|
||||
│ ├── url: "https://api.slack.com/webhook" │
|
||||
│ ├── method: "POST" │
|
||||
│ └── body: { text: "42 novos leads!" } │
|
||||
│ │
|
||||
│ Step 4: transform │
|
||||
│ ├── operation: "sum", source: "query_result" │
|
||||
│ └── output: { sum: 42 } │
|
||||
│ │
|
||||
│ Step 5: notify │
|
||||
│ ├── message: "Pipeline atualizado" │
|
||||
│ └── channel: "system" │
|
||||
│ │
|
||||
│ → status: "completed", steps_completed: 5/5 │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Integração com Outros Sistemas
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ INTEGRAÇÕES DA AUTOMAÇÃO │
|
||||
│ │
|
||||
│ ┌───────── Manus IA (GPT-4o) ─────────────────────────────┐ │
|
||||
│ │ action: "agent_task" │ │
|
||||
│ │ → manusService.run(userId, prompt) │ │
|
||||
│ │ → Polling até 120s para conclusão │ │
|
||||
│ │ → Retorna resultado do agente │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────── WhatsApp (Baileys) ──────────────────────────────┐ │
|
||||
│ │ action: "send_whatsapp" │ │
|
||||
│ │ trigger: "message_received" (xos_automations) │ │
|
||||
│ │ → Integra via CommunicationService │ │
|
||||
│ └────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────── ERPNext/SOE ─────────────────────────────────────┐ │
|
||||
│ │ action: "erp_sync" │ │
|
||||
│ │ trigger: "record.created" / "record.updated" │ │
|
||||
│ │ → Sincroniza entidades com motor SOE │ │
|
||||
│ └────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────── Webhooks Externos ───────────────────────────────┐ │
|
||||
│ │ action: "webhook" │ │
|
||||
│ │ trigger: "webhook.received" │ │
|
||||
│ │ → HTTP POST/GET com payload JSON │ │
|
||||
│ │ → Slack, Zapier, n8n, APIs externas │ │
|
||||
│ └────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────── Knowledge Graph ─────────────────────────────────┐ │
|
||||
│ │ Eventos de automação alimentam o Knowledge Graph │ │
|
||||
│ │ via comm_events → graph_nodes + graph_edges │ │
|
||||
│ └────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────── Casa de Máquinas ─────────────────────────────────┐ │
|
||||
│ │ Monitora status do Automation Engine (:8005) │ │
|
||||
│ │ GET /api/automation-engine/health │ │
|
||||
│ │ Exibe: scheduler stats, event_bus stats, workflow stats │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Arquivos-Chave
|
||||
|
||||
| Arquivo | Função | Linhas |
|
||||
|---------|--------|--------|
|
||||
| `server/python/automation_engine.py` | Motor FastAPI (Scheduler + EventBus + Workflows) | 668 |
|
||||
| `server/automations/service.ts` | AutomationService (executor + scheduler fallback) | 209 |
|
||||
| `server/automations/routes.ts` | CRUD API (/api/automations/*) | 343 |
|
||||
| `server/automations/engine-proxy.ts` | Proxy Node → Python (:8005) | 267 |
|
||||
| `shared/schema.ts` (L582-636) | 4 tabelas: automations, actions, logs, scheduled | 55 |
|
||||
| `shared/schema.ts` (L6593-6608) | xos_automations (CRM workflows) | 16 |
|
||||
| `client/src/pages/Automations.tsx` | Interface CRUD geral | 559 |
|
||||
| `client/src/pages/XosAutomations.tsx` | Interface CRM automations | 350 |
|
||||
| `client/src/pages/WorkflowBuilder.tsx` | Editor visual de workflows | 493 |
|
||||
| `client/src/components/BpmnDiagram.tsx` | Diagrama BPMN | 645 |
|
||||
| `client/src/components/ProcessDiagram.tsx` | Diagrama de processos | 184 |
|
||||
|
||||
---
|
||||
|
||||
## 12. Resumo Visual
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ARCÁDIA AUTOMAÇÕES STACK │
|
||||
│ │
|
||||
│ ┌─── Frontend ──────────────────────────────────────────────┐ │
|
||||
│ │ Automations.tsx │ XosAutomations.tsx │ WorkflowBuilder │ │
|
||||
│ │ BPMN Viewer │ ProcessDiagram │ │ │
|
||||
│ └──────────────────────────┬────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────────────────▼────────────────────────────────┐ │
|
||||
│ │ Node.js Gateway (:5000) │ │
|
||||
│ │ │ │
|
||||
│ │ /api/automations/* → CRUD (Drizzle ORM) │ │
|
||||
│ │ /api/automation-engine/* → Proxy para :8005 │ │
|
||||
│ │ AutomationService → Executor + Scheduler 60s │ │
|
||||
│ └──────────────────────────┬────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────────────────▼────────────────────────────────┐ │
|
||||
│ │ Python FastAPI (:8005) │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │
|
||||
│ │ │ Scheduler│ │Event Bus │ │Workflow Executor │ │ │
|
||||
│ │ │ 30s loop │ │ Pub/Sub │ │ 10 step types │ │ │
|
||||
│ │ │ Cron 5pt │ │ 9 events │ │ SQL/HTTP/Logic │ │ │
|
||||
│ │ └──────────┘ └──────────┘ └──────────────────┘ │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ PostgreSQL: 5 tabelas │ │
|
||||
│ │ automations, automation_actions, automation_logs, │ │
|
||||
│ │ scheduled_tasks, xos_automations │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ Integrações: Manus IA, WhatsApp, ERPNext, Webhooks, │ │
|
||||
│ │ Knowledge Graph, Casa de Máquinas │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
|
@ -0,0 +1,840 @@
|
|||
# Arcádia BI — Mapa de Business Intelligence
|
||||
|
||||
> Mapa completo da arquitetura de BI, incluindo Motor Python, Metabase,
|
||||
> Cientista (IA), ETL/Staging, APIs e Frontend.
|
||||
> Atualizado em: Março 2026
|
||||
|
||||
---
|
||||
|
||||
## 1. Visão Geral
|
||||
|
||||
O BI da Arcádia Suite opera em **4 camadas complementares** que se combinam para oferecer analytics completo: desde queries SQL diretas até dashboards visuais no Metabase, passando por análise com IA e ingestão de dados externos.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ USUÁRIO / FRONTEND │
|
||||
│ │
|
||||
│ BiWorkspace.tsx (2.970 linhas) Metabase (Embeddado) │
|
||||
│ React + Recharts + Tailwind Proxy → :8088 │
|
||||
│ 8 abas funcionais Dashboards avançados │
|
||||
│ │
|
||||
│ ┌──────────┬──────────┬──────────┬──────────┬──────────┐ │
|
||||
│ │ Overview │DataSrc │ Upload │ Datasets │ Charts │ │
|
||||
│ ├──────────┼──────────┼──────────┼──────────┼──────────┤ │
|
||||
│ │ Backups │ Staging │ Advanced │ │ │ │
|
||||
│ └──────────┴──────────┴──────────┴──────────┴──────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ 🤖 Assistente BI (IA) │ │
|
||||
│ │ Chat com GPT-4o sobre dados │ │
|
||||
│ │ Prompts rápidos + análise livre │ │
|
||||
│ └─────────────────────────────────────┘ │
|
||||
└─────────────────────────┬────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────┼───────────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────────┐
|
||||
│ API BI (Node) │ │BI Engine (Python)│ │ Metabase (Java) │
|
||||
│ /api/bi/* │ │ /api/bi-engine/* │ │ /metabase/* │
|
||||
│ │ │ │ │ │
|
||||
│ CRUD: │ │ SQL Execution │ │ Dashboards │
|
||||
│ - DataSources │ │ Chart Data Gen │ │ Perguntas/Queries │
|
||||
│ - Datasets │ │ Micro-BI │ │ Alertas │
|
||||
│ - Charts │ │ Análise Pandas │ │ Visualizações │
|
||||
│ - Dashboards │ │ Cache (TTL 5min) │ │ Coleções │
|
||||
│ - Backups │ │ Agregações │ │ Embedding │
|
||||
│ - Upload/ETL │ │ Insights Auto │ │ │
|
||||
│ - Staging │ │ │ │ │
|
||||
│ - AI Analysis │ │ │ │ │
|
||||
└────────┬────────┘ └────────┬─────────┘ └──────────┬──────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ PostgreSQL (Porta 5432) │
|
||||
│ │
|
||||
│ Tabelas de gestão BI: data_sources, bi_datasets, bi_charts, │
|
||||
│ bi_dashboards, bi_dashboard_charts, backup_jobs, backup_artifacts, │
|
||||
│ staged_tables, staging_mappings, migration_jobs │
|
||||
│ │
|
||||
│ Tabelas de negócio: persons, products, sales_orders, fin_*, │
|
||||
│ crm_*, whatsapp_messages, valuation_*, etc. │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Os 4 Pilares do BI
|
||||
|
||||
### Pilar 1 — Motor BI Python (FastAPI :8004)
|
||||
|
||||
O coração analítico. Processa SQL, gera dados para gráficos e fornece Micro-BI.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ BI ENGINE (Python 3.11) │
|
||||
│ FastAPI + Pandas + NumPy │
|
||||
│ Porta: 8004 │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ SEGURANÇA SQL │ │
|
||||
│ │ │ │
|
||||
│ │ ✓ Somente SELECT/WITH permitidos │ │
|
||||
│ │ ✓ 17 keywords bloqueadas (DROP, DELETE, INSERT...) │ │
|
||||
│ │ ✓ 7 padrões perigosos filtrados (pg_sleep, --, /*) │ │
|
||||
│ │ ✓ Multi-statement bloqueado (múltiplos ;) │ │
|
||||
│ │ ✓ Conexão read-only (autocommit) │ │
|
||||
│ │ ✓ Timeout: 30 segundos por query │ │
|
||||
│ │ ✓ Limite: 10.000 linhas por resultado │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ CACHE (QueryCache) │ │
|
||||
│ │ │ │
|
||||
│ │ • Tipo: In-memory (OrderedDict) │ │
|
||||
│ │ • Max entries: 200 │ │
|
||||
│ │ • TTL: 300 segundos (5 minutos) │ │
|
||||
│ │ • Key: SHA-256 de (SQL + params) │ │
|
||||
│ │ • Métricas: hits, misses, hit_rate (%) │ │
|
||||
│ │ • Invalidação: por padrão ou total │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ CAPABILITIES │ │
|
||||
│ │ │ │
|
||||
│ │ • sql_query → Execução de SELECT arbitrário │ │
|
||||
│ │ • chart_data → Geração de dados para gráficos │ │
|
||||
│ │ • micro_bi → Métricas rápidas com comparação │ │
|
||||
│ │ • analysis → Análise estatística com Pandas │ │
|
||||
│ │ • aggregation → Agregações customizadas │ │
|
||||
│ │ • cache → Cache inteligente de queries │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Pilar 2 — Metabase (Java :8088)
|
||||
|
||||
Plataforma visual de BI, acessível via proxy reverso.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ METABASE │
|
||||
│ Porta: 8088 (via proxy /metabase) │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ PROXY (http-proxy-middleware) │ │
|
||||
│ │ │ │
|
||||
│ │ Gateway (:5000) → /metabase/* → Metabase (:8088) │ │
|
||||
│ │ │ │
|
||||
│ │ • pathRewrite: /metabase → / │ │
|
||||
│ │ • changeOrigin: true │ │
|
||||
│ │ • timeout: 60 segundos │ │
|
||||
│ │ • Reescrita de Location headers │ │
|
||||
│ │ • Fallback: 502 "Metabase indisponível" │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ FUNCIONALIDADES │ │
|
||||
│ │ │ │
|
||||
│ │ • Dashboards visuais (drag & drop) │ │
|
||||
│ │ • Perguntas SQL / visual query builder │ │
|
||||
│ │ • Alertas automáticos │ │
|
||||
│ │ • Coleções organizadas │ │
|
||||
│ │ • Embedding de dashboards │ │
|
||||
│ │ • Filtros interativos │ │
|
||||
│ │ • Exportação (CSV, Excel, PDF) │ │
|
||||
│ │ • Agendamento de relatórios │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ CONEXÃO │ │
|
||||
│ │ │ │
|
||||
│ │ Banco: PostgreSQL (mesmo DATABASE_URL do Gateway) │ │
|
||||
│ │ Acesso: Todas as tabelas public (read-only) │ │
|
||||
│ │ Config: METABASE_HOST, METABASE_PORT │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Pilar 3 — API BI Node.js (Gateway :5000)
|
||||
|
||||
Camada de gestão e CRUD para objetos de BI.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ API BI (Node.js/Express) │
|
||||
│ Registrada no Gateway (:5000) │
|
||||
│ │
|
||||
│ ┌── Gestão de Objetos BI ──────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ Data Sources → Conexões com bancos externos │ │
|
||||
│ │ Datasets → Conjuntos de dados (table/SQL/API) │ │
|
||||
│ │ Charts → Gráficos salvos (config + dados) │ │
|
||||
│ │ Dashboards → Painéis com layout de charts │ │
|
||||
│ │ Backup Jobs → Jobs de backup programados │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── ETL & Ingestão ────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ Upload → Importação de arquivos │ │
|
||||
│ │ Staging → Área de preparação para migração │ │
|
||||
│ │ Migration → Jobs de migração de dados │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── Tabelas Internas ──────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ Exploração de schema do PostgreSQL │ │
|
||||
│ │ Criação de datasets a partir de tabelas internas │ │
|
||||
│ │ Categorização automática (Sistema, CRM, BI, etc.) │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── Análise com IA ────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ OpenAI GPT-4o → Responde perguntas sobre dados │ │
|
||||
│ │ Prompts pré-definidos (Resumo, Anomalias, etc.) │ │
|
||||
│ │ Formato JSON estruturado (answer + insights) │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Pilar 4 — Cientista (Python: Auto-Programação)
|
||||
|
||||
Módulo de IA para análise avançada e geração de código.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ CIENTISTA (Python) │
|
||||
│ python-service/services/cientista.py │
|
||||
│ │
|
||||
│ ┌── Análise Estatística ───────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ analyze_data() │ │
|
||||
│ │ → Shape (rows × columns) │ │
|
||||
│ │ → Tipos de dados por coluna │ │
|
||||
│ │ → Valores ausentes │ │
|
||||
│ │ → Estatísticas (describe): mean, std, min, max │ │
|
||||
│ │ → Amostra dos dados (5 primeiros registros) │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── Detecção de Padrões ──────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ detect_patterns() │ │
|
||||
│ │ → Correlações fortes (> 0.7) entre colunas │ │
|
||||
│ │ → Tendências (crescente/decrescente) via polyfit │ │
|
||||
│ │ → Saída: [{ type, columns, value, description }] │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── Insights Automáticos ─────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ generate_insights() │ │
|
||||
│ │ → Valores ausentes por coluna (% de missing) │ │
|
||||
│ │ → Detecção de outliers (IQR × 1.5) │ │
|
||||
│ │ → Valores dominantes (> 50% em categóricas) │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── Auto-Programação (ScientistModule) ────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ generate_analysis_code(description, goal) │ │
|
||||
│ │ → Templates: aggregate, filter, predict, correlate │ │
|
||||
│ │ → Gera código Python executável │ │
|
||||
│ │ → Salva padrões aprendidos │ │
|
||||
│ │ → Histórico de execuções │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. API REST Completa
|
||||
|
||||
### 3.1 — BI Engine Proxy (/api/bi-engine/*)
|
||||
|
||||
Proxy do Gateway para o Motor Python FastAPI (:8004):
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/bi-engine/health` | Status do motor (online/offline, banco, cache) |
|
||||
| GET | `/api/bi-engine/version` | Versão e capabilities |
|
||||
| GET | `/api/bi-engine/metrics` | Métricas (cache stats, limites) |
|
||||
| GET | `/api/bi-engine/tables` | Lista todas as tabelas do PostgreSQL |
|
||||
| GET | `/api/bi-engine/tables/:name/columns` | Schema de colunas de uma tabela |
|
||||
| GET | `/api/bi-engine/tables/:name/preview` | Preview dos dados (limit padrão: 50) |
|
||||
| GET | `/api/bi-engine/tables/:name/stats` | Estatísticas (row_count, column_count) |
|
||||
| POST | `/api/bi-engine/query` | Executa SQL (somente SELECT/WITH) |
|
||||
| POST | `/api/bi-engine/chart-data` | Gera dados formatados para gráficos |
|
||||
| POST | `/api/bi-engine/micro-bi` | Micro-BI: métricas rápidas com comparação temporal |
|
||||
| POST | `/api/bi-engine/analyze` | Análise de dados com Pandas |
|
||||
| POST | `/api/bi-engine/aggregate` | Agregações customizadas |
|
||||
| GET | `/api/bi-engine/cache/stats` | Estatísticas do cache |
|
||||
| POST | `/api/bi-engine/cache/invalidate` | Invalida cache (por padrão ou total) |
|
||||
|
||||
### 3.2 — BI Management (/api/bi/*)
|
||||
|
||||
CRUD de objetos BI no Gateway Node.js:
|
||||
|
||||
#### Data Sources
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/bi/data-sources` | Lista fontes de dados do usuário |
|
||||
| POST | `/api/bi/data-sources` | Cria fonte (PostgreSQL, MySQL, MongoDB, SQLite, Internal) |
|
||||
| POST | `/api/bi/data-sources/:id/test` | Testa conexão |
|
||||
| DELETE | `/api/bi/data-sources/:id` | Remove fonte |
|
||||
|
||||
#### Datasets
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/bi/datasets` | Lista datasets do usuário |
|
||||
| POST | `/api/bi/datasets` | Cria dataset (table, SQL ou API) |
|
||||
| POST | `/api/bi/datasets/:id/execute` | Executa dataset e retorna dados |
|
||||
| DELETE | `/api/bi/datasets/:id` | Remove dataset |
|
||||
|
||||
#### Charts
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/bi/charts` | Lista gráficos do usuário |
|
||||
| POST | `/api/bi/charts` | Cria gráfico (bar, line, pie, area, scatter, table, metric, donut) |
|
||||
| DELETE | `/api/bi/charts/:id` | Remove gráfico |
|
||||
|
||||
#### Dashboards
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/bi/dashboards` | Lista dashboards do usuário |
|
||||
| GET | `/api/bi/dashboards/:id` | Detalhe do dashboard + charts posicionados |
|
||||
| POST | `/api/bi/dashboards` | Cria dashboard |
|
||||
| POST | `/api/bi/dashboards/:id/charts` | Adiciona chart ao dashboard (com posição X/Y/W/H) |
|
||||
| DELETE | `/api/bi/dashboards/:id` | Remove dashboard |
|
||||
|
||||
#### Tabelas Internas
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/bi/internal-tables` | Lista tabelas do sistema (com tamanho e categoria) |
|
||||
| GET | `/api/bi/internal-tables/:name/schema` | Schema + preview + row count |
|
||||
| POST | `/api/bi/internal-tables/:name/create-dataset` | Cria dataset a partir de tabela interna |
|
||||
| POST | `/api/bi/query` | Query direta em tabela (simples, limite 1000) |
|
||||
|
||||
#### Backups
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/bi/backup-jobs` | Lista jobs de backup |
|
||||
| POST | `/api/bi/backup-jobs` | Cria job (full, schema, data, incremental) |
|
||||
| POST | `/api/bi/backup-jobs/:id/run` | Executa backup |
|
||||
| DELETE | `/api/bi/backup-jobs/:id` | Remove job |
|
||||
| GET | `/api/bi/backup-artifacts` | Lista artefatos gerados |
|
||||
|
||||
### 3.3 — Upload & ETL (/api/upload/*, /api/staging/*)
|
||||
|
||||
#### Upload de Dados
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| POST | `/api/upload/file` | Upload de arquivo (CSV, JSON, Excel, SQL, BSON, ZIP) |
|
||||
| POST | `/api/upload/analyze` | Analisa arquivo e sugere schema |
|
||||
|
||||
Formatos suportados (até 200MB):
|
||||
- `.csv`, `.txt` → Parser CSV inteligente (suporta aspas)
|
||||
- `.json` → Parse direto de arrays/objetos
|
||||
- `.xlsx`, `.xls` → Leitura via SheetJS (XLSX)
|
||||
- `.sql` → Detecção de INSERTs e criação de datasets
|
||||
- `.bson` → Parse via biblioteca BSON
|
||||
- `.zip` → Extração e processamento de conteúdo
|
||||
|
||||
#### Staging (Área de Preparação)
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/staging/tables` | Lista tabelas staged |
|
||||
| GET | `/api/staging/tables/:id` | Detalhe de tabela staged |
|
||||
| GET | `/api/staging/tables/:id/data` | Dados da tabela staged |
|
||||
| POST | `/api/staging/tables/:id/mappings` | Cria mapeamento de colunas |
|
||||
| GET | `/api/staging/tables/:id/mappings` | Lista mapeamentos |
|
||||
| POST | `/api/staging/tables/:id/migrate` | Executa migração para tabela destino |
|
||||
| DELETE | `/api/staging/tables/:id` | Remove tabela staged |
|
||||
|
||||
### 3.4 — Metabase Proxy (/metabase/*)
|
||||
|
||||
| Rota | Destino | Função |
|
||||
|------|---------|--------|
|
||||
| `/metabase/*` | `http://localhost:8088/*` | Proxy reverso completo para Metabase |
|
||||
|
||||
---
|
||||
|
||||
## 4. Banco de Dados — Tabelas do BI
|
||||
|
||||
### Objetos BI
|
||||
|
||||
```
|
||||
┌─────────────────────────────┐ ┌──────────────────────────────┐
|
||||
│ data_sources │ │ bi_datasets │
|
||||
│ │ │ │
|
||||
│ id, userId, name, │ │ id, userId, name, description,│
|
||||
│ type (postgresql|mysql| │ │ dataSourceId, queryType │
|
||||
│ mongodb|sqlite|internal), │ │ (table|sql|api), tableName, │
|
||||
│ host, port, database, │ │ sqlQuery, columns, filters, │
|
||||
│ username, password, │ │ isPublic, createdAt, updatedAt│
|
||||
│ connectionString, isActive, │ │ │
|
||||
│ lastTestedAt, createdAt │ └───────────────┬───────────────┘
|
||||
└──────────────────────────────┘ │ 1:N
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ bi_charts │
|
||||
│ │
|
||||
│ id, userId, name, datasetId, chartType (bar|line|pie|area| │
|
||||
│ scatter|table|metric|donut), config (JSON), xAxis, yAxis, │
|
||||
│ groupBy, aggregation, colors, createdAt, updatedAt │
|
||||
└──────────────────────────┬──────────────────────────────────────┘
|
||||
│ N:M (via bi_dashboard_charts)
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ bi_dashboards bi_dashboard_charts │
|
||||
│ │
|
||||
│ id, userId, name, id, dashboardId, │
|
||||
│ description, layout (JSON), chartId, │
|
||||
│ isPublic, createdAt, updatedAt positionX, positionY, │
|
||||
│ width, height │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Backups
|
||||
|
||||
```
|
||||
┌───────────────────────────────┐ ┌───────────────────────────────┐
|
||||
│ backup_jobs │ │ backup_artifacts │
|
||||
│ │ │ │
|
||||
│ id, userId, name, │ │ id, backupJobId, filename, │
|
||||
│ dataSourceId, backupType │ │ filePath, fileSize, status │
|
||||
│ (full|schema|data|incremental),│ │ (running|completed|failed), │
|
||||
│ includeSchema, includeTables, │ │ startedAt, completedAt, │
|
||||
│ excludeTables, compressionType,│ │ errorMessage │
|
||||
│ retentionDays, storageLocation,│ │ │
|
||||
│ lastRunAt, isActive, createdAt │ │ │
|
||||
└───────────────────────────────┘ └───────────────────────────────┘
|
||||
```
|
||||
|
||||
### Staging & Migração
|
||||
|
||||
```
|
||||
┌───────────────────────────────┐ ┌───────────────────────────────┐
|
||||
│ staged_tables │ │ staging_mappings │
|
||||
│ │ │ │
|
||||
│ id, userId, name, │ │ id, stagedTableId, │
|
||||
│ originalFilename, fileType, │ │ sourceColumn, targetTable, │
|
||||
│ rowCount, columnCount, │ │ targetColumn, transformType, │
|
||||
│ columns (JSON), sampleData, │ │ transformConfig, createdAt │
|
||||
│ status, createdAt, updatedAt │ │ │
|
||||
└───────────────────────────────┘ └───────────────────────────────┘
|
||||
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ migration_jobs │
|
||||
│ │
|
||||
│ id, userId, name, stagedTableId, targetTable, status, │
|
||||
│ (pending|running|completed|failed), totalRows, processedRows, │
|
||||
│ errorLog, mappingConfig (JSON), createdAt, updatedAt │
|
||||
└───────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Frontend — BiWorkspace.tsx (2.970 linhas)
|
||||
|
||||
### Tabs
|
||||
|
||||
| Tab | Valor | Descrição |
|
||||
|-----|-------|-----------|
|
||||
| **Overview** | `overview` | KPIs resumo: DataSources, Datasets, Charts, Dashboards, Backups |
|
||||
| **Data Sources** | `datasources` | CRUD de fontes externas (PostgreSQL, MySQL, MongoDB, SQLite) + tabelas internas do sistema |
|
||||
| **Upload** | `upload` | Importação de arquivos (CSV, Excel, JSON, SQL, BSON, ZIP) com preview automático |
|
||||
| **Datasets** | `datasets` | Conjuntos de dados criados a partir de tabelas ou SQL, com execução e preview |
|
||||
| **Charts** | `charts` | Criação de gráficos (8 tipos) a partir de datasets, com visualização Recharts |
|
||||
| **Backups** | `backups` | Jobs de backup com execução e artefatos gerados |
|
||||
| **Staging** | `staging` | Área de preparação: mapeamento de colunas, migração para tabelas destino |
|
||||
| **Advanced** | `advanced` | BI Engine (link para Motor Python), Metabase (link para iframe), Assistente IA |
|
||||
|
||||
### Tipos de Gráficos Suportados (Recharts)
|
||||
|
||||
| Tipo | Componente | Uso |
|
||||
|------|-----------|-----|
|
||||
| `bar` | `BarChart + Bar` | Comparação entre categorias |
|
||||
| `line` | `LineChart + Line` | Evolução temporal |
|
||||
| `pie` | `PieChart + Pie + Cell` | Proporções (até 8 fatias) |
|
||||
| `area` | `AreaChart + Area` | Tendências com preenchimento |
|
||||
| `scatter` | (planejado) | Correlação entre variáveis |
|
||||
| `table` | HTML Table | Dados tabulares |
|
||||
| `metric` | Card KPI | Valor único com destaque |
|
||||
| `donut` | `PieChart (innerRadius)` | Proporções com centro vazio |
|
||||
|
||||
### Assistente BI (IA)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 🤖 Assistente BI │
|
||||
│ │
|
||||
│ • Chat conversacional com GPT-4o │
|
||||
│ • Recebe dados do dataset ativo como contexto │
|
||||
│ • Prompts rápidos pré-definidos: │
|
||||
│ 📊 "Resumo Executivo" (principais métricas e insights) │
|
||||
│ 🔍 "Anomalias" (outliers e desvios) │
|
||||
│ 📈 "Tendências" (padrões temporais) │
|
||||
│ 💡 "Recomendações" (ações sugeridas) │
|
||||
│ • Formato de resposta: JSON { answer, insights[] } │
|
||||
│ • Interface: chat bubble com histórico │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Categorias de Tabelas Internas
|
||||
|
||||
O BI mapeia automaticamente as tabelas do sistema em categorias:
|
||||
|
||||
| Categoria | Tabelas |
|
||||
|-----------|---------|
|
||||
| **Sistema** | users, applications, roles |
|
||||
| **Process Compass** | pc_clients, pc_projects, pc_tasks |
|
||||
| **CRM** | crm_clients, crm_contracts, crm_partners, crm_opportunities, crm_leads, crm_messages |
|
||||
| **BI** | bi_datasets, bi_charts |
|
||||
| **Conhecimento** | knowledge_base |
|
||||
| **Manus** | manus_runs, agent_tasks |
|
||||
| **Comunicação** | whatsapp_messages, conversations |
|
||||
| **Outros** | Todas as demais tabelas (valuation_*, fin_*, etc.) |
|
||||
|
||||
---
|
||||
|
||||
## 6. Motor BI Python — Endpoints Detalhados
|
||||
|
||||
### POST /query — Execução SQL
|
||||
|
||||
```json
|
||||
{
|
||||
"sql": "SELECT status, COUNT(*) as total FROM sales_orders GROUP BY status",
|
||||
"params": {},
|
||||
"limit": 1000,
|
||||
"use_cache": true
|
||||
}
|
||||
```
|
||||
|
||||
Retorno:
|
||||
```json
|
||||
{
|
||||
"data": [{"status": "confirmed", "total": 45}, ...],
|
||||
"columns": [{"name": "status", "type": "1043"}, ...],
|
||||
"row_count": 3,
|
||||
"elapsed_ms": 12.5,
|
||||
"cached": false
|
||||
}
|
||||
```
|
||||
|
||||
### POST /chart-data — Dados para Gráficos
|
||||
|
||||
```json
|
||||
{
|
||||
"table": "sales_orders",
|
||||
"x_axis": "created_at",
|
||||
"y_axis": "total_amount",
|
||||
"aggregation": "sum",
|
||||
"time_grain": "month",
|
||||
"group_by": "status",
|
||||
"filters": [{"column": "status", "operator": "!=", "value": "cancelled"}],
|
||||
"limit": 12
|
||||
}
|
||||
```
|
||||
|
||||
Retorno:
|
||||
```json
|
||||
{
|
||||
"labels": ["2026-01", "2026-02", "2026-03"],
|
||||
"series": {
|
||||
"confirmed": [{"label": "2026-01", "value": 15000}, ...],
|
||||
"delivered": [{"label": "2026-01", "value": 12000}, ...]
|
||||
},
|
||||
"row_count": 6,
|
||||
"elapsed_ms": 18.3,
|
||||
"query": "SELECT DATE_TRUNC('month', created_at) AS label, status AS series, SUM(total_amount) AS value FROM sales_orders WHERE status != 'cancelled' GROUP BY ...",
|
||||
"cached": false
|
||||
}
|
||||
```
|
||||
|
||||
### POST /micro-bi — Métricas Rápidas
|
||||
|
||||
```json
|
||||
{
|
||||
"table": "fin_transactions",
|
||||
"metrics": ["count", "sum:amount", "avg:amount"],
|
||||
"dimension": "category",
|
||||
"period": "month",
|
||||
"compare_previous": true
|
||||
}
|
||||
```
|
||||
|
||||
Retorno:
|
||||
```json
|
||||
{
|
||||
"current": [
|
||||
{"dimension": "vendas", "count": 120, "sum_amount": 45000, "avg_amount": 375}
|
||||
],
|
||||
"previous": [
|
||||
{"dimension": "vendas", "count": 98, "sum_amount": 38000, "avg_amount": 387.76}
|
||||
],
|
||||
"comparison": {
|
||||
"count": {"current": 120, "previous": 98, "change": 22, "change_pct": 22.4, "trend": "up"}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### POST /analyze — Análise com Pandas
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [{"col1": 10, "col2": "A"}, ...],
|
||||
"question": "Quais são os outliers?"
|
||||
}
|
||||
```
|
||||
|
||||
Retorno: estatísticas por coluna (min, max, mean, median, std, sum), insights automáticos, sugestões de gráficos.
|
||||
|
||||
### Granularidades Temporais (time_grain)
|
||||
|
||||
| Valor | Função SQL | Exemplo |
|
||||
|-------|-----------|---------|
|
||||
| `day` | `DATE_TRUNC('day', col)` | 2026-03-16 |
|
||||
| `week` | `DATE_TRUNC('week', col)` | 2026-03-11 |
|
||||
| `month` | `DATE_TRUNC('month', col)` | 2026-03-01 |
|
||||
| `quarter` | `DATE_TRUNC('quarter', col)` | 2026-01-01 |
|
||||
| `year` | `DATE_TRUNC('year', col)` | 2026-01-01 |
|
||||
|
||||
### Funções de Agregação
|
||||
|
||||
| Valor | SQL | Uso |
|
||||
|-------|-----|-----|
|
||||
| `sum` | `SUM(col)` | Totalizador |
|
||||
| `avg` | `AVG(col)` | Média |
|
||||
| `count` | `COUNT(*)` | Contagem |
|
||||
| `min` | `MIN(col)` | Mínimo |
|
||||
| `max` | `MAX(col)` | Máximo |
|
||||
|
||||
---
|
||||
|
||||
## 7. Pipeline ETL — Fluxo de Ingestão
|
||||
|
||||
```
|
||||
Arquivo (CSV/Excel/JSON/SQL/BSON/ZIP)
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ 1. UPLOAD (/api/upload/file) │
|
||||
│ │
|
||||
│ • Multer (disk storage) │
|
||||
│ • Limite: 200 MB │
|
||||
│ • Validação de extensão │
|
||||
│ • Parse do conteúdo │
|
||||
└──────────────────┬───────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ 2. STAGING (staged_tables) │
|
||||
│ │
|
||||
│ • Armazena headers e sample data │
|
||||
│ • Detecta tipos de colunas │
|
||||
│ • Preview para o usuário │
|
||||
└──────────────────┬───────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ 3. MAPEAMENTO (staging_mappings) │
|
||||
│ │
|
||||
│ • sourceColumn → targetColumn │
|
||||
│ • transformType (cast, rename, etc.)│
|
||||
│ • Validação de compatibilidade │
|
||||
└──────────────────┬───────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ 4. MIGRAÇÃO (migration_jobs) │
|
||||
│ │
|
||||
│ • Cria tabela destino se necessário │
|
||||
│ • INSERT em lotes │
|
||||
│ • Tracking: total/processed rows │
|
||||
│ • Status: pending → running → │
|
||||
│ completed/failed │
|
||||
│ • Error log para debugging │
|
||||
└──────────────────┬───────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ 5. DATASET (bi_datasets) │
|
||||
│ │
|
||||
│ • Dataset pronto para visualização │
|
||||
│ • Pode criar charts e dashboards │
|
||||
│ • Conecta com BI Engine e Metabase │
|
||||
└──────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Integração entre Pilares
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ FLUXO DE DADOS │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ Upload │────▶│ Staging │────▶│ Dataset │────▶│ Chart │ │
|
||||
│ │ (ETL) │ │(Prepare) │ │ (Dados) │ │ (Visual) │ │
|
||||
│ └──────────┘ └──────────┘ └────┬─────┘ └────┬─────┘ │
|
||||
│ │ │ │
|
||||
│ ┌─────▼──────┐ ┌────▼─────┐ │
|
||||
│ │ BI Engine │ │Dashboard │ │
|
||||
│ │ (Análise) │ │(Painel) │ │
|
||||
│ └─────┬──────┘ └──────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────┐ ┌─────▼──────┐ │
|
||||
│ │ Tabelas │─────────────────────▶│ Metabase │ │
|
||||
│ │ Internas │ │ (Visual) │ │
|
||||
│ │ (PG) │ └──────┬─────┘ │
|
||||
│ └──────────┘ │ │
|
||||
│ ┌─────▼──────┐ │
|
||||
│ │ Cientista │ │
|
||||
│ │ (IA/ML) │ │
|
||||
│ └────────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Como os pilares se conectam:
|
||||
|
||||
| De | Para | Como |
|
||||
|----|------|------|
|
||||
| Upload | Staging | Arquivo parseado → tabela staged |
|
||||
| Staging | Dataset | Migração → dataset criado automaticamente |
|
||||
| Tabela Interna | Dataset | `/api/bi/internal-tables/:name/create-dataset` |
|
||||
| Dataset | BI Engine | SQL do dataset executado no Motor Python |
|
||||
| Dataset | Chart | Chart referencia datasetId |
|
||||
| Chart | Dashboard | `bi_dashboard_charts` com posição (X,Y,W,H) |
|
||||
| Tabelas PG | Metabase | Conexão direta ao PostgreSQL |
|
||||
| Dataset | Assistente IA | Dados enviados como contexto ao GPT-4o |
|
||||
| Dados | Cientista | `analyze_data()`, `detect_patterns()`, `generate_insights()` |
|
||||
|
||||
---
|
||||
|
||||
## 9. Configuração e Variáveis de Ambiente
|
||||
|
||||
| Variável | Default | Usado por | Descrição |
|
||||
|----------|---------|-----------|-----------|
|
||||
| `DATABASE_URL` | - | BI Engine, Gateway | Conexão PostgreSQL |
|
||||
| `BI_ENGINE_HOST` | `localhost` | Engine Proxy | Host do motor Python |
|
||||
| `BI_PORT` | `8004` | BI Engine, Proxy | Porta do motor Python |
|
||||
| `BI_ENGINE_TIMEOUT` | `30000` | Engine Proxy | Timeout de proxy (ms) |
|
||||
| `METABASE_HOST` | `localhost` | Metabase Proxy | Host do Metabase |
|
||||
| `METABASE_PORT` | `8088` | Metabase Proxy | Porta do Metabase |
|
||||
| `METABASE_TIMEOUT` | `60000` | Metabase Proxy | Timeout do proxy (ms) |
|
||||
| `OPENAI_API_KEY` | - | Assistente BI | API key para GPT-4o |
|
||||
|
||||
---
|
||||
|
||||
## 10. Arquivos-Chave
|
||||
|
||||
| Arquivo | Camada | Função | Linhas |
|
||||
|---------|--------|--------|--------|
|
||||
| `server/python/bi_engine.py` | Motor Python | SQL, Charts, Micro-BI, Cache, Análise | ~650 |
|
||||
| `server/bi/routes.ts` | API Node | CRUD de DataSources, Datasets, Charts, Dashboards, Backups | ~700 |
|
||||
| `server/bi/engine-proxy.ts` | Proxy | Proxy Gateway → BI Engine Python (:8004) | ~200 |
|
||||
| `server/bi/upload.ts` | ETL | Upload de arquivos (CSV, Excel, JSON, SQL, BSON, ZIP) | ~1.060 |
|
||||
| `server/bi/staging.ts` | ETL | Staging, mapeamento, migração de dados | ~408 |
|
||||
| `server/metabase/proxy.ts` | Proxy | Proxy Gateway → Metabase (:8088) | ~42 |
|
||||
| `client/src/pages/BiWorkspace.tsx` | Frontend | Interface principal do BI (8 tabs) | ~2.970 |
|
||||
| `client/src/pages/MetabaseProxyPage.tsx` | Frontend | Página do Metabase embeddado | ~25 |
|
||||
| `python-service/services/cientista.py` | IA/ML | Análise, padrões, insights, auto-programação | ~567 |
|
||||
| `shared/schema.ts` | Schema | Tabelas BI (9 tabelas) | Parte do schema |
|
||||
|
||||
---
|
||||
|
||||
## 11. Deploy (Docker/Coolify)
|
||||
|
||||
### Container `arcadia-bi` (Motor Python)
|
||||
|
||||
```dockerfile
|
||||
FROM python:3.11-slim
|
||||
WORKDIR /app
|
||||
RUN pip install fastapi uvicorn pydantic pandas numpy psycopg2-binary
|
||||
COPY server/python/bi_engine.py ./main.py
|
||||
EXPOSE 8004
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8004"]
|
||||
```
|
||||
|
||||
### Container `metabase`
|
||||
|
||||
```yaml
|
||||
metabase:
|
||||
image: metabase/metabase:latest
|
||||
restart: always
|
||||
ports:
|
||||
- "8088:3000"
|
||||
environment:
|
||||
MB_DB_TYPE: postgres
|
||||
MB_DB_DBNAME: metabase
|
||||
MB_DB_PORT: 5432
|
||||
MB_DB_USER: ${PGUSER:-arcadia}
|
||||
MB_DB_PASS: ${PGPASSWORD}
|
||||
MB_DB_HOST: postgres
|
||||
JAVA_TIMEZONE: America/Sao_Paulo
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- metabase-data:/metabase-data
|
||||
networks:
|
||||
- arcadia
|
||||
```
|
||||
|
||||
### Variáveis no Gateway (Docker)
|
||||
|
||||
```yaml
|
||||
gateway:
|
||||
environment:
|
||||
BI_ENGINE_HOST: bi
|
||||
BI_PORT: 8004
|
||||
METABASE_HOST: metabase
|
||||
METABASE_PORT: 3000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Resumo Visual
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ARCÁDIA BI STACK │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ FRONTEND: BiWorkspace.tsx + Recharts + Assistente IA (GPT) │ │
|
||||
│ └────────────────────────────┬────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────┼─────────────────────┐ │
|
||||
│ │ │ │ │
|
||||
│ ┌──────▼──────┐ ┌────────▼────────┐ ┌────────▼────────┐ │
|
||||
│ │ API BI │ │ BI Engine │ │ Metabase │ │
|
||||
│ │ (Node.js) │ │ (Python:8004) │ │ (Java:8088) │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ CRUD │ │ SQL + Charts │ │ Dashboards │ │
|
||||
│ │ Upload/ETL │ │ Micro-BI │ │ Visual Builder │ │
|
||||
│ │ Staging │ │ Análise Pandas │ │ Alertas │ │
|
||||
│ │ Backups │ │ Cache (5min) │ │ Exportação │ │
|
||||
│ └──────┬──────┘ └────────┬────────┘ └────────┬────────┘ │
|
||||
│ │ │ │ │
|
||||
│ └────────────────────┼──────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────▼──────────┐ │
|
||||
│ │ PostgreSQL │ │
|
||||
│ │ (Porta 5432) │ │
|
||||
│ │ │ │
|
||||
│ │ 9 tabelas BI + │ │
|
||||
│ │ 100+ tabelas SOE │ │
|
||||
│ └────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ CIENTISTA (Python) — Análise, Padrões, Insights, AutoCode │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
|
@ -0,0 +1,834 @@
|
|||
# Arcádia CRM — Mapa Completo do Sistema de Relacionamento
|
||||
|
||||
> Mapa da arquitetura CRM nativa da Arcádia Suite: módulos, tabelas,
|
||||
> APIs, comunicação unificada (WhatsApp/Email), motor de comissões,
|
||||
> integração Frappe/ERPNext e frontend.
|
||||
> Atualizado em: Março 2026
|
||||
|
||||
---
|
||||
|
||||
## 1. Visão Geral
|
||||
|
||||
O CRM da Arcádia Suite é **100% nativo** — construído internamente em Node.js + PostgreSQL com Drizzle ORM. Ele opera em **três camadas** que se complementam, unificadas por um Motor de Comunicação e alimentadas por IA (Manus/GPT-4o):
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ CAMADA DE EXPERIÊNCIA │
|
||||
│ │
|
||||
│ ┌────────────────────────┐ ┌───────────────────────┐ │
|
||||
│ │ Arcádia CRM (Crm.tsx) │ │ XOS CRM (XosCrm.tsx) │ │
|
||||
│ │ 2.700 linhas │ │ 752 linhas │ │
|
||||
│ │ 12 abas │ │ Kanban visual │ │
|
||||
│ │ Gestão completa │ │ Deals + Pipeline │ │
|
||||
│ └────────────┬───────────┘ └───────────┬───────────┘ │
|
||||
│ │ │ │
|
||||
│ └──────────┬────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────────────────▼───────────────────────────────────┐ │
|
||||
│ │ WhatsApp.tsx — Interface de Atendimento │ │
|
||||
│ │ Multi-sessão, tickets, IA auto-reply │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
└──────────────────────────┬───────────────────────────────────────────┘
|
||||
│
|
||||
┌──────────────────────────▼───────────────────────────────────────────┐
|
||||
│ CAMADA DE ORQUESTRAÇÃO │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ Routes │ │ Storage │ │ Comm. │ │Commission│ │ WA │ │
|
||||
│ │ (1.592L) │ │ (708L) │ │ (289L) │ │ Engine │ │ Bridge │ │
|
||||
│ │ │ │ │ │ │ │ (418L) │ │ (126L) │ │
|
||||
│ │ 90+ │ │CrmStorage│ │ Connect/ │ │ Revenue │ │ Sessão │ │
|
||||
│ │ endpoints│ │ class │ │ Send/ │ │ Schedule │ │ ↔ Canal │ │
|
||||
│ │ │ │ │ │ Receive │ │ Comissões│ │ Eventos │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ Frappe Svc │ │Google Calendar│ │ Knowledge │ │
|
||||
│ │ ERPNext sync │ │ OAuth + Sync │ │ Graph Feed │ │
|
||||
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
||||
└──────────────────────────┬───────────────────────────────────────────┘
|
||||
│
|
||||
┌──────────────────────────▼───────────────────────────────────────────┐
|
||||
│ CAMADA DE DADOS │
|
||||
│ │
|
||||
│ PostgreSQL — 30 tabelas CRM (crm_*) + 15 tabelas XOS (xos_*) │
|
||||
│ + 8 tabelas Communication (comm_*) │
|
||||
│ │
|
||||
│ Drizzle ORM — Schema em shared/schema.ts │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Os Três CRMs
|
||||
|
||||
### 2.1 — Arcádia CRM (Principal)
|
||||
|
||||
O CRM completo da plataforma. Gerencia todo o ciclo de relacionamento: da captação de leads até contratos e comissões.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ ARCÁDIA CRM │
|
||||
│ client/src/pages/Crm.tsx (2.700 linhas) │
|
||||
│ /api/crm/* (90+ endpoints) │
|
||||
│ │
|
||||
│ 12 Abas: │
|
||||
│ ┌──────────┬──────────┬──────────┬──────────┐ │
|
||||
│ │Dashboard │ Leads │ Opps │ Produtos │ │
|
||||
│ ├──────────┼──────────┼──────────┼──────────┤ │
|
||||
│ │Parceiros │ Clientes │Contratos │Mensagens │ │
|
||||
│ ├──────────┼──────────┼──────────┼──────────┤ │
|
||||
│ │Calendário│Integrações│Config │Multitenant│ │
|
||||
│ └──────────┴──────────┴──────────┴──────────┘ │
|
||||
│ │
|
||||
│ Domínios: │
|
||||
│ • Pipeline de Vendas (Leads → Opps → Propostas → Contratos)│
|
||||
│ • Parceiros & Certificações │
|
||||
│ • Produtos & Catálogo │
|
||||
│ • Comunicação Multicanal (WhatsApp, Email) │
|
||||
│ • Comissões & Revenue Schedule │
|
||||
│ • Agenda (Google Calendar) │
|
||||
│ • Integração Frappe/ERPNext │
|
||||
│ • Multi-tenant │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 — XOS CRM (Kanban/Pipeline)
|
||||
|
||||
Visão simplificada e visual focada em gestão de deals com drag & drop.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ XOS CRM │
|
||||
│ client/src/pages/XosCrm.tsx (752 linhas) │
|
||||
│ │
|
||||
│ Foco: │
|
||||
│ • Kanban visual de deals │
|
||||
│ • Pipeline com estágios customizáveis │
|
||||
│ • Contatos & Empresas │
|
||||
│ • Conversas & Tickets │
|
||||
│ • Campanhas & Automações │
|
||||
│ • Filas de atendimento │
|
||||
│ │
|
||||
│ Tabelas próprias: xos_contacts, xos_companies, │
|
||||
│ xos_pipelines, xos_pipeline_stages, xos_deals, │
|
||||
│ xos_conversations, xos_messages, xos_tickets, │
|
||||
│ xos_campaigns, xos_automations, xos_activities, │
|
||||
│ xos_queues, xos_queue_users, xos_internal_notes, │
|
||||
│ xos_quick_messages │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.3 — Plus CRM (Legado/PHP)
|
||||
|
||||
Módulo CRM dentro do Arcádia Plus (Laravel), focado em anotações e histórico.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ PLUS CRM (Legado) │
|
||||
│ plus/app/Http/Controllers/CrmController │
|
||||
│ │
|
||||
│ • Anotações de clientes (crm_anotacoes) │
|
||||
│ • Histórico de interações │
|
||||
│ • Integrado ao ERP Plus (fiscal, estoque, financeiro) │
|
||||
│ • Proxy reverso via /plus │
|
||||
│ • Banco MySQL (separado do PostgreSQL) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Ciclo de Vida do Cliente
|
||||
|
||||
```
|
||||
┌────────┐ ┌────────┐ ┌────────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ LEAD │───▶│ OPP │───▶│ PROPOSTA │───▶│ CONTRATO │───▶│ CLIENTE │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ Nome │ │ Valor │ │ Itens │ │ Receita │ │ Ativo │
|
||||
│ Email │ │ Stage │ │ Preços │ │ Parcelas │ │ Suporte │
|
||||
│ Fone │ │ Prob% │ │ Desconto │ │ Comissão │ │ Upsell │
|
||||
│ Source │ │ Close │ │ Aprovação │ │ Marcos │ │ │
|
||||
│ Status │ │ Owner │ │ PDF │ │ Duração │ │ │
|
||||
└────┬───┘ └────┬───┘ └────────────┘ └──────────┘ └──────────┘
|
||||
│ │
|
||||
│ convert │ won/lost
|
||||
│ │
|
||||
▼ ▼
|
||||
POST /leads/ POST /opportunities/
|
||||
:id/convert :id/won
|
||||
:id/lost
|
||||
:id/bill
|
||||
:id/open-project
|
||||
```
|
||||
|
||||
### Fluxo detalhado:
|
||||
|
||||
| Etapa | Ação | Endpoint | Resultado |
|
||||
|-------|------|----------|-----------|
|
||||
| 1 | Captação | `POST /api/crm/leads` | Lead criado (status: new) |
|
||||
| 2 | Qualificação | `PATCH /api/crm/leads/:id` | Lead atualizado (status: qualified) |
|
||||
| 3 | Conversão | `POST /api/crm/leads/:id/convert` | Cria Opportunity + Client automaticamente |
|
||||
| 4 | Negociação | `PATCH /api/crm/opportunities/:id/stage` | Move entre estágios do pipeline |
|
||||
| 5 | Proposta | `POST /api/crm/proposals` | Proposta com itens e preços |
|
||||
| 6 | Ganho | `POST /api/crm/opportunities/:id/won` | Marca como ganha |
|
||||
| 7 | Contrato | `POST /api/crm/contracts` | Contrato com receita e marcos |
|
||||
| 8 | Faturamento | `POST /api/crm/opportunities/:id/bill` | Gera faturamento |
|
||||
| 9 | Projeto | `POST /api/crm/opportunities/:id/open-project` | Abre projeto no Process Compass |
|
||||
| 10 | Perda | `POST /api/crm/opportunities/:id/lost` | Marca como perdida + motivo |
|
||||
|
||||
---
|
||||
|
||||
## 4. Comunicação Multicanal
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ SISTEMA DE COMUNICAÇÃO │
|
||||
│ │
|
||||
│ ┌───────────┐ ┌────────────────┐ ┌──────────────────┐ │
|
||||
│ │ WhatsApp │ │ Communication │ │ CRM Threads │ │
|
||||
│ │ (Baileys) │───▶│ Service │───▶│ & Messages │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ Multi- │ │ connectChannel │ │ crm_channels │ │
|
||||
│ │ sessão │ │ sendMessage │ │ crm_threads │ │
|
||||
│ │ QR Code │ │ receiveMessage │ │ crm_messages │ │
|
||||
│ │ Auto-reply│ │ getOrCreate │ │ crm_quick_msgs │ │
|
||||
│ │ (GPT-4o) │ │ Thread │ │ │ │
|
||||
│ └─────┬─────┘ └────────────────┘ └──────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────▼─────────────────────────────────────┐ │
|
||||
│ │ WhatsApp Bridge │ │
|
||||
│ │ │ │
|
||||
│ │ Evento "message" → handleIncoming │ │
|
||||
│ │ Evento "connected"→ updateChannelStatus │ │
|
||||
│ │ Evento "qr" → storeQRCode │ │
|
||||
│ │ Evento "disconnect"→ markDisconnected │ │
|
||||
│ │ │ │
|
||||
│ │ sessionChannelMap: Session ↔ CRM Channel │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────┐ │
|
||||
│ │ Motor de Comunicação (:8006) │ │
|
||||
│ │ │ │
|
||||
│ │ Unifica: │ │
|
||||
│ │ • CRM Threads (crm_threads) │ │
|
||||
│ │ • XOS Conversations (xos_conversations) │ │
|
||||
│ │ • WhatsApp Tickets (xos_tickets) │ │
|
||||
│ │ │ │
|
||||
│ │ Tabelas comm_*: │ │
|
||||
│ │ comm_contacts, comm_threads, │ │
|
||||
│ │ comm_messages, comm_channels, │ │
|
||||
│ │ comm_queues, comm_quick_messages, │ │
|
||||
│ │ comm_events → Knowledge Graph │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Fluxo de mensagem WhatsApp:
|
||||
|
||||
```
|
||||
Cliente envia mensagem no WhatsApp
|
||||
│
|
||||
▼
|
||||
Baileys (whatsappService) recebe
|
||||
│
|
||||
▼ emite evento "message"
|
||||
WhatsApp Bridge captura
|
||||
│
|
||||
▼ mapeia session → channelId
|
||||
CommunicationService.receiveMessage()
|
||||
│
|
||||
├──▶ getOrCreateThread() → cria/reutiliza thread
|
||||
│
|
||||
├──▶ crmMessages → INSERT (direction: "incoming")
|
||||
│
|
||||
├──▶ AI Auto-Reply? (GPT-4o-mini em horário comercial)
|
||||
│
|
||||
└──▶ Knowledge Graph → graph_nodes + graph_edges
|
||||
```
|
||||
|
||||
### Canais suportados:
|
||||
|
||||
| Canal | Tecnologia | Status |
|
||||
|-------|-----------|--------|
|
||||
| **WhatsApp** | Baileys (multi-device) | Ativo |
|
||||
| **Email** | SMTP + IMAP | Planejado |
|
||||
| **SMS** | API (Twilio/etc.) | Futuro |
|
||||
| **Telegram** | Bot API | Futuro |
|
||||
| **Chat Interno** | Socket.IO | Ativo (Comunidades) |
|
||||
|
||||
---
|
||||
|
||||
## 5. Motor de Comissões
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ COMMISSION ENGINE │
|
||||
│ server/crm/commission-engine.ts (418L) │
|
||||
│ │
|
||||
│ ┌── Revenue Schedule ──────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ generateRevenueSchedule(contractId) │ │
|
||||
│ │ │ │
|
||||
│ │ • Mensal: projeta N parcelas (até 24 meses) │ │
|
||||
│ │ • Anual: projeta N anos (até 10 anos) │ │
|
||||
│ │ • Calcula datas de vencimento │ │
|
||||
│ │ • Status: pending → paid → overdue │ │
|
||||
│ │ │ │
|
||||
│ │ Contrato R$ 5.000/mês × 12 meses: │ │
|
||||
│ │ → 12 registros em crm_revenue_schedule │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── Commission Rules ─────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ crm_commission_rules │ │
|
||||
│ │ │ │
|
||||
│ │ Regras baseadas em: │ │
|
||||
│ │ • Tipo de receita: recurring / one-time │ │
|
||||
│ │ • Cenário de venda: direct / partner │ │
|
||||
│ │ • Mês do contrato (faixas) │ │
|
||||
│ │ │ │
|
||||
│ │ Exemplo: │ │
|
||||
│ │ Meses 1-5 (aquisição): 15% sobre receita │ │
|
||||
│ │ Meses 6-12 (retenção): 10% sobre receita │ │
|
||||
│ │ Meses 13+ (maturidade): 5% sobre receita │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── Cálculo de Comissões ─────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ calculateCommissions(contractId) │ │
|
||||
│ │ │ │
|
||||
│ │ Para cada parcela da revenue schedule: │ │
|
||||
│ │ 1. Identifica o mês do contrato │ │
|
||||
│ │ 2. Busca regra aplicável (tipo + cenário + faixa) │ │
|
||||
│ │ 3. Calcula valor = parcela × percentual │ │
|
||||
│ │ 4. Cria registro em crm_commissions │ │
|
||||
│ │ │ │
|
||||
│ │ Status: pending → approved → paid │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Integração Frappe/ERPNext
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ FRAPPE SERVICE │
|
||||
│ server/crm/frappe-service.ts │
|
||||
│ │
|
||||
│ Autenticação: token apiKey:apiSecret │
|
||||
│ Base URL: configurável por conector │
|
||||
│ │
|
||||
│ Sync Bidirecional: │
|
||||
│ │
|
||||
│ Arcádia CRM ←──────────→ ERPNext/Frappe │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────┐ │
|
||||
│ │ Entidade Arcádia DocType Frappe │ │
|
||||
│ │ ─────────────────── ───────────────── │ │
|
||||
│ │ crm_leads → Lead │ │
|
||||
│ │ crm_opportunities→ Opportunity │ │
|
||||
│ │ crm_products → Item │ │
|
||||
│ │ crm_partners → Supplier (tipo: partner) │ │
|
||||
│ │ crm_clients → Customer │ │
|
||||
│ └──────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Funcionalidades: │
|
||||
│ • POST /frappe/connectors → Cria conector │
|
||||
│ • POST /frappe/connectors/:id/test → Testa conexão │
|
||||
│ • POST /frappe/connectors/:id/sync → Sincroniza dados │
|
||||
│ • GET /frappe/connectors/:id/logs → Logs de sync │
|
||||
│ • CRUD /frappe/connectors/:id/mappings → Mapeamentos │
|
||||
│ │
|
||||
│ Tabelas: crm_frappe_connectors, crm_frappe_mappings │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Google Calendar
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ GOOGLE CALENDAR INTEGRATION │
|
||||
│ server/crm/google-calendar.ts │
|
||||
│ │
|
||||
│ OAuth 2.0 Flow: │
|
||||
│ GET /api/crm/google/auth → Inicia OAuth │
|
||||
│ GET /api/crm/google/callback → Recebe token │
|
||||
│ GET /api/crm/google/status → Status da conexão │
|
||||
│ POST /api/crm/google/disconnect → Desconecta │
|
||||
│ POST /api/crm/google/sync → Sincroniza eventos │
|
||||
│ GET /api/crm/google/events → Lista eventos │
|
||||
│ POST /api/crm/google/events → Cria evento │
|
||||
│ │
|
||||
│ Tabela: crm_google_tokens │
|
||||
│ Tabela: crm_events (sincronizado) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Banco de Dados — Todas as Tabelas
|
||||
|
||||
### 8.1 — Tabelas CRM (crm_*) — 30 tabelas
|
||||
|
||||
#### Entidades Principais
|
||||
|
||||
```
|
||||
┌──────────────────────────┐ ┌──────────────────────────┐
|
||||
│ crm_leads │ │ crm_clients │
|
||||
│ │ │ │
|
||||
│ id, tenantId, name, │ │ id, tenantId, name, │
|
||||
│ email, phone, company, │ │ email, phone, company, │
|
||||
│ source, status (new| │ │ type, address, city, │
|
||||
│ contacted|qualified| │ │ state, notes, segment, │
|
||||
│ proposal|won|lost), │ │ isActive, createdAt, │
|
||||
│ notes, assignedTo, │ │ updatedAt │
|
||||
│ estimatedValue, │ │ │
|
||||
│ createdAt, updatedAt │ └──────────────────────────┘
|
||||
└──────────────────────────┘
|
||||
|
||||
┌──────────────────────────┐ ┌──────────────────────────┐
|
||||
│ crm_opportunities │ │ crm_partners │
|
||||
│ │ │ │
|
||||
│ id, tenantId, name, │ │ id, tenantId, companyName,│
|
||||
│ clientId, value, │ │ contactName, email, phone,│
|
||||
│ probability, stageId, │ │ type (reseller|referral| │
|
||||
│ expectedCloseDate, │ │ integrator|consultant), │
|
||||
│ status (open|won|lost), │ │ status (active|inactive| │
|
||||
│ lostReason, assignedTo, │ │ suspended), tier (gold| │
|
||||
│ notes, approvalStatus, │ │ silver|bronze|platinum), │
|
||||
│ approvedBy, createdAt │ │ commissionRate, region, │
|
||||
│ │ │ specializations, notes │
|
||||
└──────────────────────────┘ └──────────────────────────┘
|
||||
```
|
||||
|
||||
#### Pipeline e Propostas
|
||||
|
||||
```
|
||||
┌───────────────────────────┐ ┌───────────────────────────┐
|
||||
│ crm_pipeline_stages │ │ crm_proposals │
|
||||
│ │ │ │
|
||||
│ id, tenantId, name, │ │ id, tenantId, opportunityId│
|
||||
│ order, color, isDefault, │ │ title, description, │
|
||||
│ probability, createdAt │ │ totalValue, discount, │
|
||||
│ │ │ validUntil, status (draft| │
|
||||
│ │ │ sent|approved|rejected), │
|
||||
│ │ │ approvedBy, notes, │
|
||||
│ │ │ createdAt, updatedAt │
|
||||
└───────────────────────────┘ └───────────────────────────┘
|
||||
|
||||
┌───────────────────────────┐
|
||||
│ crm_proposal_items │
|
||||
│ │
|
||||
│ id, proposalId, productId, │
|
||||
│ description, quantity, │
|
||||
│ unitPrice, discount, │
|
||||
│ totalPrice, createdAt │
|
||||
└───────────────────────────┘
|
||||
```
|
||||
|
||||
#### Contratos e Financeiro
|
||||
|
||||
```
|
||||
┌───────────────────────────┐ ┌───────────────────────────┐
|
||||
│ crm_contracts │ │ crm_revenue_schedule │
|
||||
│ │ │ │
|
||||
│ id, tenantId, name, │ │ id, contractId, month, │
|
||||
│ clientId, partnerId, │ │ dueDate, value, status │
|
||||
│ type (recurring|one_time), │ │ (pending|paid|overdue), │
|
||||
│ totalValue, monthlyValue, │ │ paidDate, createdAt │
|
||||
│ startDate, endDate, │ │ │
|
||||
│ billingCycle (monthly| │ └───────────────────────────┘
|
||||
│ yearly), status (draft| │
|
||||
│ active|completed|cancelled)│ ┌───────────────────────────┐
|
||||
│ autoRenew, notes, │ │ crm_contract_milestones │
|
||||
│ createdAt, updatedAt │ │ │
|
||||
└───────────────────────────┘ │ id, contractId, name, │
|
||||
│ description, dueDate, │
|
||||
┌───────────────────────────┐ │ status (pending|in_progress│
|
||||
│ crm_commission_rules │ │ |completed|delayed), │
|
||||
│ │ │ completedAt, notes │
|
||||
│ id, tenantId, name, │ └───────────────────────────┘
|
||||
│ revenueType (recurring| │
|
||||
│ one_time), saleType │
|
||||
│ (direct|partner), │
|
||||
│ percentage, monthFrom, │
|
||||
│ monthTo, isActive │
|
||||
└───────────────────────────┘
|
||||
|
||||
┌───────────────────────────┐
|
||||
│ crm_commissions │
|
||||
│ │
|
||||
│ id, tenantId, contractId, │
|
||||
│ partnerId, userId, │
|
||||
│ revenueScheduleId, month, │
|
||||
│ baseValue, percentage, │
|
||||
│ commissionValue, status │
|
||||
│ (pending|approved|paid), │
|
||||
│ paidDate, createdAt │
|
||||
└───────────────────────────┘
|
||||
```
|
||||
|
||||
#### Comunicação
|
||||
|
||||
```
|
||||
┌───────────────────────────┐ ┌───────────────────────────┐
|
||||
│ crm_channels │ │ crm_threads │
|
||||
│ │ │ │
|
||||
│ id, tenantId, name, │ │ id, tenantId, channelId, │
|
||||
│ type (whatsapp|email|sms| │ │ contactPhone, contactName, │
|
||||
│ telegram), status │ │ status (open|waiting| │
|
||||
│ (connected|disconnected| │ │ resolved|closed), │
|
||||
│ pending_qr), identifier, │ │ assignedTo, priority, │
|
||||
│ qrCode, sessionData, │ │ subject, tags, unreadCount,│
|
||||
│ lastConnectedAt, │ │ lastMessageAt, closedAt, │
|
||||
│ createdAt, updatedAt │ │ createdAt, updatedAt │
|
||||
└───────────────────────────┘ └───────────────────────────┘
|
||||
|
||||
┌───────────────────────────┐ ┌───────────────────────────┐
|
||||
│ crm_messages │ │ crm_quick_messages │
|
||||
│ │ │ │
|
||||
│ id, threadId, direction │ │ id, tenantId, title, │
|
||||
│ (incoming|outgoing), │ │ content, shortcut, │
|
||||
│ content, contentType │ │ category, isActive, │
|
||||
│ (text|image|audio|video| │ │ createdAt │
|
||||
│ document), externalId, │ │ │
|
||||
│ status (sent|delivered| │ │ Uso: /atalho → mensagem │
|
||||
│ read|failed), senderName, │ │ rápida pré-definida │
|
||||
│ metadata, createdAt │ │ │
|
||||
└───────────────────────────┘ └───────────────────────────┘
|
||||
```
|
||||
|
||||
#### Outros
|
||||
|
||||
```
|
||||
┌───────────────────────────┐ ┌───────────────────────────┐
|
||||
│ crm_products │ │ crm_opportunity_products │
|
||||
│ │ │ │
|
||||
│ id, tenantId, name, sku, │ │ id, opportunityId, │
|
||||
│ description, category, │ │ productId, quantity, │
|
||||
│ unitPrice, costPrice, │ │ unitPrice, discount, │
|
||||
│ type (product|service| │ │ totalPrice │
|
||||
│ subscription), isActive │ └───────────────────────────┘
|
||||
└───────────────────────────┘
|
||||
|
||||
┌───────────────────────────┐ ┌───────────────────────────┐
|
||||
│ crm_campaigns │ │ crm_campaign_contacts │
|
||||
│ │ │ │
|
||||
│ id, tenantId, name, type, │ │ id, campaignId, contactId, │
|
||||
│ status, channelId, │ │ status (pending|sent| │
|
||||
│ message, scheduledAt, │ │ delivered|failed|replied), │
|
||||
│ sentCount, deliveredCount, │ │ sentAt, deliveredAt, │
|
||||
│ failedCount, createdAt │ │ repliedAt │
|
||||
└───────────────────────────┘ └───────────────────────────┘
|
||||
|
||||
┌───────────────────────────┐ ┌───────────────────────────┐
|
||||
│ crm_events │ │ crm_google_tokens │
|
||||
│ │ │ │
|
||||
│ id, tenantId, title, │ │ id, userId, accessToken, │
|
||||
│ description, type, │ │ refreshToken, expiresAt, │
|
||||
│ startDate, endDate, │ │ calendarId, createdAt │
|
||||
│ isAllDay, location, │ │ │
|
||||
│ attendees, googleEventId, │ └───────────────────────────┘
|
||||
│ color, reminders, │
|
||||
│ createdBy, createdAt │ ┌───────────────────────────┐
|
||||
│ │ │ crm_opportunity_registrations│
|
||||
└───────────────────────────┘ │ │
|
||||
│ id, opportunityId, partnerId,│
|
||||
┌───────────────────────────┐ │ status (pending|approved| │
|
||||
│ crm_partner_certifications │ │ rejected), registeredAt │
|
||||
│ │ └───────────────────────────┘
|
||||
│ id, partnerId, name, │
|
||||
│ issuer, certDate, expDate, │ ┌───────────────────────────┐
|
||||
│ status, documentUrl │ │ crm_partner_performance │
|
||||
│ │ │ │
|
||||
└───────────────────────────┘ │ id, partnerId, period, │
|
||||
│ revenue, deals, activeContracts│
|
||||
│ satisfaction, churnRate │
|
||||
└───────────────────────────┘
|
||||
```
|
||||
|
||||
### 8.2 — Tabelas XOS CRM (xos_*) — 15 tabelas
|
||||
|
||||
```
|
||||
xos_contacts → Contatos unificados (nome, email, phone, company, tags)
|
||||
xos_companies → Empresas (nome, CNPJ, segmento, tamanho, receita)
|
||||
xos_pipelines → Pipelines customizáveis
|
||||
xos_pipeline_stages → Estágios de pipeline (nome, ordem, cor, prob%)
|
||||
xos_deals → Negócios/Deals (valor, estágio, expected close)
|
||||
xos_conversations → Conversas (canal, status, prioridade)
|
||||
xos_messages → Mensagens individuais
|
||||
xos_tickets → Tickets de suporte (SLA, prioridade)
|
||||
xos_campaigns → Campanhas de marketing
|
||||
xos_automations → Regras de automação (trigger, action)
|
||||
xos_activities → Atividades (calls, emails, meetings, tasks)
|
||||
xos_queues → Filas de atendimento
|
||||
xos_queue_users → Usuários nas filas
|
||||
xos_internal_notes → Notas internas de deals/contatos
|
||||
xos_quick_messages → Mensagens rápidas/templates
|
||||
```
|
||||
|
||||
### 8.3 — Tabelas Comunicação Unificada (comm_*) — 8 tabelas
|
||||
|
||||
```
|
||||
comm_channels → Canais unificados
|
||||
comm_contacts → Contatos unificados (merge CRM + XOS + WA)
|
||||
comm_threads → Threads unificadas
|
||||
comm_messages → Mensagens unificadas
|
||||
comm_queues → Filas unificadas
|
||||
comm_queue_members → Membros das filas
|
||||
comm_quick_messages → Templates unificados
|
||||
comm_events → Eventos (alimentam Knowledge Graph)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. API REST Completa (/api/crm/*)
|
||||
|
||||
### Parceiros
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/partners` | Lista parceiros |
|
||||
| GET | `/partners/:id` | Detalhe do parceiro |
|
||||
| POST | `/partners` | Cria parceiro |
|
||||
| PATCH | `/partners/:id` | Atualiza parceiro |
|
||||
| DELETE | `/partners/:id` | Remove parceiro |
|
||||
| GET | `/partners/:id/certifications` | Certificações |
|
||||
| POST | `/partners/:id/certifications` | Adiciona certificação |
|
||||
| GET | `/partners/:id/performance` | Performance |
|
||||
| POST | `/partners/:id/performance` | Registra performance |
|
||||
| POST | `/partners/:id/convert-to-client` | Converte para cliente |
|
||||
|
||||
### Clientes
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/clients` | Lista clientes |
|
||||
| GET | `/clients/:id` | Detalhe do cliente |
|
||||
| POST | `/clients` | Cria cliente |
|
||||
| PATCH | `/clients/:id` | Atualiza cliente |
|
||||
| DELETE | `/clients/:id` | Remove cliente |
|
||||
|
||||
### Leads
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/leads` | Lista leads |
|
||||
| GET | `/leads/:id` | Detalhe do lead |
|
||||
| POST | `/leads` | Cria lead |
|
||||
| PATCH | `/leads/:id` | Atualiza lead |
|
||||
| DELETE | `/leads/:id` | Remove lead |
|
||||
| POST | `/leads/:id/convert` | Converte para Opportunity + Client |
|
||||
|
||||
### Oportunidades
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/opportunities` | Lista oportunidades |
|
||||
| GET | `/opportunities/:id` | Detalhe |
|
||||
| POST | `/opportunities` | Cria oportunidade |
|
||||
| PATCH | `/opportunities/:id` | Atualiza |
|
||||
| DELETE | `/opportunities/:id` | Remove |
|
||||
| PATCH | `/opportunities/:id/stage` | Muda estágio do pipeline |
|
||||
| POST | `/opportunities/:id/won` | Marca como ganha |
|
||||
| POST | `/opportunities/:id/lost` | Marca como perdida |
|
||||
| POST | `/opportunities/:id/approve` | Aprova (governance) |
|
||||
| POST | `/opportunities/:id/reject` | Rejeita |
|
||||
| POST | `/opportunities/:id/open-project` | Abre projeto no Compass |
|
||||
| POST | `/opportunities/:id/bill` | Gera faturamento |
|
||||
| GET | `/opportunities/:id/products` | Produtos da opp |
|
||||
| POST | `/opportunities/:id/products` | Adiciona produto |
|
||||
| DELETE | `/opportunity-products/:id` | Remove produto |
|
||||
| GET | `/opportunities/:id/proposals` | Propostas da opp |
|
||||
|
||||
### Propostas
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/proposals` | Lista propostas |
|
||||
| GET | `/proposals/:id` | Detalhe |
|
||||
| POST | `/proposals` | Cria proposta |
|
||||
| PATCH | `/proposals/:id` | Atualiza |
|
||||
| DELETE | `/proposals/:id` | Remove |
|
||||
| GET | `/proposals/:id/items` | Itens da proposta |
|
||||
| POST | `/proposals/:id/items` | Adiciona item |
|
||||
| PATCH | `/proposal-items/:id` | Atualiza item |
|
||||
| DELETE | `/proposal-items/:id` | Remove item |
|
||||
|
||||
### Contratos
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/contracts` | Lista contratos |
|
||||
| GET | `/contracts/:id` | Detalhe |
|
||||
| POST | `/contracts` | Cria contrato |
|
||||
| PATCH | `/contracts/:id` | Atualiza |
|
||||
| GET | `/contracts/:id/revenue` | Revenue schedule |
|
||||
| GET | `/contracts/:id/milestones` | Marcos |
|
||||
| POST | `/contracts/:id/milestones` | Adiciona marco |
|
||||
| PATCH | `/milestones/:id` | Atualiza marco |
|
||||
| DELETE | `/milestones/:id` | Remove marco |
|
||||
| POST | `/contracts/:id/process-commissions` | Calcula comissões |
|
||||
| POST | `/contracts/:id/extend-schedule` | Estende schedule |
|
||||
| POST | `/contracts/extend-all` | Estende todos |
|
||||
|
||||
### Comissões
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/commission-rules` | Lista regras |
|
||||
| POST | `/commission-rules` | Cria regra |
|
||||
| POST | `/commission-rules/seed` | Seed regras padrão |
|
||||
| GET | `/commissions` | Lista comissões |
|
||||
| GET | `/commissions/summary` | Resumo financeiro |
|
||||
| POST | `/commissions/:id/mark-paid` | Marca como paga |
|
||||
|
||||
### Comunicação
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/channels` | Lista canais |
|
||||
| POST | `/channels` | Cria canal |
|
||||
| PATCH | `/channels/:id` | Atualiza canal |
|
||||
| POST | `/channels/:id/whatsapp/connect` | Conecta WhatsApp |
|
||||
| POST | `/channels/whatsapp/new` | Novo canal WhatsApp |
|
||||
| POST | `/channels/:id/whatsapp/disconnect` | Desconecta |
|
||||
| GET | `/threads` | Lista threads |
|
||||
| GET | `/threads/:id` | Detalhe da thread |
|
||||
| POST | `/threads` | Cria thread |
|
||||
| PATCH | `/threads/:id` | Atualiza thread |
|
||||
| GET | `/threads/:id/messages` | Mensagens da thread |
|
||||
| POST | `/threads/:id/messages` | Envia mensagem |
|
||||
| POST | `/threads/:id/send` | Envia via WhatsApp |
|
||||
| POST | `/threads/:id/read` | Marca como lida |
|
||||
| POST | `/threads/:id/assign` | Atribui a agente |
|
||||
| POST | `/threads/:id/close` | Fecha thread |
|
||||
| POST | `/threads/:id/reopen` | Reabre thread |
|
||||
| GET | `/threads/stats` | Estatísticas de threads |
|
||||
| GET | `/quick-messages` | Mensagens rápidas |
|
||||
| POST | `/quick-messages` | Cria mensagem rápida |
|
||||
|
||||
### Produtos
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/products` | Lista produtos |
|
||||
| GET | `/products/:id` | Detalhe |
|
||||
| POST | `/products` | Cria produto |
|
||||
| PATCH | `/products/:id` | Atualiza |
|
||||
| DELETE | `/products/:id` | Remove |
|
||||
|
||||
### Pipeline
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/pipeline-stages` | Lista estágios |
|
||||
| POST | `/pipeline-stages` | Cria estágio |
|
||||
| PATCH | `/pipeline-stages/:id` | Atualiza |
|
||||
| DELETE | `/pipeline-stages/:id` | Remove |
|
||||
|
||||
### Eventos e Calendário
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/events` | Lista eventos |
|
||||
| POST | `/events` | Cria evento |
|
||||
| PATCH | `/events/:id` | Atualiza |
|
||||
| PUT | `/events/:id` | Atualiza (full) |
|
||||
| DELETE | `/events/:id` | Remove |
|
||||
| GET | `/google/auth` | Inicia OAuth Google |
|
||||
| GET | `/google/callback` | Callback OAuth |
|
||||
| GET | `/google/status` | Status conexão |
|
||||
| POST | `/google/disconnect` | Desconecta Google |
|
||||
| POST | `/google/sync` | Sincroniza eventos |
|
||||
| GET | `/google/events` | Eventos Google |
|
||||
| POST | `/google/events` | Cria evento Google |
|
||||
|
||||
### Frappe/ERPNext
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/frappe/connectors` | Lista conectores |
|
||||
| GET | `/frappe/connectors/:id` | Detalhe |
|
||||
| POST | `/frappe/connectors` | Cria conector |
|
||||
| PATCH | `/frappe/connectors/:id` | Atualiza |
|
||||
| DELETE | `/frappe/connectors/:id` | Remove |
|
||||
| POST | `/frappe/connectors/:id/test` | Testa conexão |
|
||||
| POST | `/frappe/connectors/:id/sync` | Sincroniza dados |
|
||||
| GET | `/frappe/connectors/:id/logs` | Logs de sync |
|
||||
| GET | `/frappe/connectors/:id/mappings` | Mapeamentos |
|
||||
| POST | `/frappe/connectors/:id/mappings` | Cria mapeamento |
|
||||
|
||||
### Estatísticas
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/stats` | KPIs gerais |
|
||||
| GET | `/stats/sales` | Estatísticas de vendas |
|
||||
|
||||
**Total: 90+ endpoints**
|
||||
|
||||
---
|
||||
|
||||
## 10. Arquivos-Chave
|
||||
|
||||
| Arquivo | Função | Linhas |
|
||||
|---------|--------|--------|
|
||||
| `client/src/pages/Crm.tsx` | Interface principal (12 tabs) | 2.700 |
|
||||
| `client/src/pages/XosCrm.tsx` | CRM Kanban/Pipeline | 752 |
|
||||
| `client/src/pages/WhatsApp.tsx` | Interface WhatsApp | — |
|
||||
| `server/crm/routes.ts` | API REST (90+ endpoints) | 1.592 |
|
||||
| `server/crm/storage.ts` | CrmStorage class (Drizzle ORM) | 708 |
|
||||
| `server/crm/communication.ts` | CommunicationService | 289 |
|
||||
| `server/crm/commission-engine.ts` | CommissionEngine | 418 |
|
||||
| `server/crm/whatsapp-bridge.ts` | WhatsAppBridge | 126 |
|
||||
| `server/crm/frappe-service.ts` | Integração ERPNext | — |
|
||||
| `server/crm/google-calendar.ts` | Google Calendar OAuth | — |
|
||||
| `server/whatsapp/service.ts` | WhatsApp (Baileys) | — |
|
||||
| `server/communication/engine.ts` | Motor Comunicação (:8006) | — |
|
||||
| `shared/schema.ts` | 53 tabelas CRM/XOS/Comm | — |
|
||||
|
||||
---
|
||||
|
||||
## 11. Resumo Visual
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ARCÁDIA CRM STACK │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ FRONTEND │ │
|
||||
│ │ Crm.tsx (12 tabs) + XosCrm.tsx (Kanban) + WhatsApp.tsx │ │
|
||||
│ └────────────────────────────┬────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌────────────────────────────▼────────────────────────────────┐ │
|
||||
│ │ API: /api/crm/* — 90+ endpoints │ │
|
||||
│ │ Router → CrmStorage → PostgreSQL │ │
|
||||
│ └───┬─────────┬─────────┬──────────┬──────────┬──────────────┘ │
|
||||
│ │ │ │ │ │ │
|
||||
│ ┌───▼───┐ ┌───▼───┐ ┌───▼────┐ ┌───▼───┐ ┌───▼────────┐ │
|
||||
│ │Comm │ │Comissão│ │WhatsApp│ │Frappe │ │Google Cal │ │
|
||||
│ │Service│ │Engine │ │Bridge │ │Service│ │OAuth │ │
|
||||
│ └───┬───┘ └───────┘ └───┬────┘ └───────┘ └────────────┘ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────▼──────┐ │
|
||||
│ │ │ Baileys │ │
|
||||
│ │ │ WhatsApp │ │
|
||||
│ │ │ Multi-Sess│ │
|
||||
│ │ └─────┬──────┘ │
|
||||
│ │ │ │
|
||||
│ ┌───▼────────────────────▼──────────────────────────────────┐ │
|
||||
│ │ Motor de Comunicação (:8006) │ │
|
||||
│ │ Unifica: CRM + XOS + WhatsApp → comm_* tables │ │
|
||||
│ │ Alimenta: Knowledge Graph + Manus IA │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ PostgreSQL: 53 tabelas (30 crm_* + 15 xos_* + 8 comm_*) │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
|
@ -0,0 +1,734 @@
|
|||
# Arcádia Suite — Mapa de Microserviços para Coolify/Docker
|
||||
|
||||
> Documento de referência para containerização e deploy em Coolify.
|
||||
> Atualizado em: Março 2026
|
||||
|
||||
---
|
||||
|
||||
## 1. Visão Geral da Arquitetura
|
||||
|
||||
```
|
||||
┌──────────────────────────────┐
|
||||
│ COOLIFY / TRAEFIK │
|
||||
│ Reverse Proxy + TLS + DNS │
|
||||
└──────────┬───────────────────┘
|
||||
│
|
||||
┌─────────────────────┼─────────────────────┐
|
||||
│ │ │
|
||||
┌────────▼────────┐ ┌────────▼────────┐ ┌─────────▼────────┐
|
||||
│ arcadia-gateway │ │ arcadia-plus │ │ arcadia-frontend│
|
||||
│ (Node.js:5000) │ │ (PHP/Nginx) │ │ (Nginx/Static) │
|
||||
│ API + WebSocket │ │ :8080 │ │ :80 (React SPA) │
|
||||
└────────┬────────┘ └────────┬────────┘ └──────────────────┘
|
||||
│ │
|
||||
┌─────────┼──────────────┬──────┘
|
||||
│ │ │
|
||||
┌───▼──┐ ┌───▼───┐ ┌────────▼────────┐
|
||||
│Postgres│ │MySQL │ │ Redis (futuro) │
|
||||
│ :5432 │ │ :3306 │ │ :6379 │
|
||||
└───┬────┘ └───────┘ └─────────────────┘
|
||||
│
|
||||
│ (DATABASE_URL compartilhado)
|
||||
│
|
||||
┌───▼──────────────────────────────────────────────────────┐
|
||||
│ MOTORES (FastAPI / Node) │
|
||||
│ │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌──────┐ ┌──────┐ ┌──────────┐ │
|
||||
│ │ Fisco │ │Contábil │ │ BI │ │Auto │ │ Comm │ │
|
||||
│ │ :8002 │ │ :8003 │ │:8004 │ │:8005 │ │ :8006 │ │
|
||||
│ │ Python │ │ Python │ │Python│ │Python│ │ Node/TS │ │
|
||||
│ └─────────┘ └─────────┘ └──────┘ └──────┘ └──────────┘ │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Inventário de Serviços (9 containers + 3 bancos)
|
||||
|
||||
### 2.1 — `arcadia-gateway` (Container Principal)
|
||||
|
||||
| Item | Valor |
|
||||
|------|-------|
|
||||
| **Tecnologia** | Node.js 20 + Express + Socket.IO |
|
||||
| **Porta** | 5000 |
|
||||
| **Entrada** | `server/index.ts` (build: `dist/index.cjs`) |
|
||||
| **Banco** | PostgreSQL (Drizzle ORM) |
|
||||
| **Responsabilidades** | API REST principal, autenticação/sessões, WebSocket (chat + WhatsApp), proxy para motores, Dev Center (6 agentes XOS), Manus IA (OpenAI), Knowledge Graph, Learning System, Governance, MCP/A2A protocols |
|
||||
| **Variáveis obrigatórias** | `DATABASE_URL`, `PGHOST`, `PGUSER`, `PGPASSWORD`, `PGDATABASE`, `SESSION_SECRET`, `OPENAI_API_KEY`, `GITHUB_TOKEN` |
|
||||
| **Variáveis opcionais** | `ERPNEXT_URL`, `ERPNEXT_API_KEY`, `ERPNEXT_API_SECRET`, `GITHUB_OWNER`, `GITHUB_REPO` |
|
||||
| **Healthcheck** | `GET /api/health` ou `GET /` |
|
||||
| **Dockerfile existente** | `./Dockerfile` (node:20-alpine, multi-stage) |
|
||||
|
||||
```dockerfile
|
||||
# Build: npm run build → dist/index.cjs
|
||||
# Run: node dist/index.cjs
|
||||
# Portas: 5000
|
||||
```
|
||||
|
||||
### 2.2 — `arcadia-frontend` (SPA React)
|
||||
|
||||
| Item | Valor |
|
||||
|------|-------|
|
||||
| **Tecnologia** | React 18 + Vite + TypeScript + Tailwind + shadcn/ui |
|
||||
| **Build** | `npm run build` → `dist/public/` |
|
||||
| **Servir com** | Nginx (static files) ou incluir no Gateway |
|
||||
| **Porta** | 80/443 (via Coolify/Traefik) |
|
||||
|
||||
> **Nota**: Atualmente o frontend é servido pelo Gateway (Express serve os assets estáticos). Para microsserviços, pode ser separado em um container Nginx leve ou mantido no Gateway.
|
||||
|
||||
**Opção A — Junto com Gateway** (recomendado para início):
|
||||
O Dockerfile atual já faz isso. O Express serve o React build.
|
||||
|
||||
**Opção B — Separado** (para escalar independentemente):
|
||||
```dockerfile
|
||||
FROM node:20-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json* ./
|
||||
RUN npm install --legacy-peer-deps
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=builder /app/dist/public /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80
|
||||
```
|
||||
|
||||
### 2.3 — `arcadia-plus` (ERP Laravel/PHP)
|
||||
|
||||
| Item | Valor |
|
||||
|------|-------|
|
||||
| **Tecnologia** | PHP 8.2 + Laravel + Nginx |
|
||||
| **Porta** | 8080 (interno) → exposto via proxy `/plus` |
|
||||
| **Banco** | MySQL 8.0 (separado do PostgreSQL) |
|
||||
| **Entrada** | `plus/public/index.php` via `php artisan serve` ou PHP-FPM + Nginx |
|
||||
| **Dockerfile existente** | `plus/Dockerfile` (php:8.2-fpm) |
|
||||
| **docker-compose existente** | `plus/docker-compose.yml` (app + web + db) |
|
||||
| **Dependências** | Composer, Node.js 18 (para assets) |
|
||||
| **Variáveis obrigatórias** | `DB_HOST`, `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD`, `MYSQL_ROOT_PASSWORD`, `APP_KEY`, `APP_URL` |
|
||||
| **Módulos** | NF-e/NFC-e/CT-e/MDF-e, PDV, Menu Digital, Ordem de Serviço, Financeiro, Estoque, CRM |
|
||||
|
||||
**Containers necessários para o Plus**:
|
||||
1. `arcadia-plus-app` — PHP-FPM (porta 9000 interna)
|
||||
2. `arcadia-plus-web` — Nginx (porta 8080, proxy para PHP-FPM)
|
||||
3. `arcadia-mysql` — MySQL 8.0 (porta 3306)
|
||||
|
||||
### 2.4 — `arcadia-fisco` (Motor Fiscal)
|
||||
|
||||
| Item | Valor |
|
||||
|------|-------|
|
||||
| **Tecnologia** | Python 3.11 + FastAPI + Uvicorn |
|
||||
| **Porta** | 8002 |
|
||||
| **Arquivo** | `server/python/fisco_service.py` |
|
||||
| **Banco** | Nenhum direto (opera sobre XML) |
|
||||
| **Dependências Python** | `fastapi`, `uvicorn`, `nfelib`, `signxml`, `lxml`, `zeep`, `cryptography`, `pydantic` |
|
||||
| **Responsabilidades** | Geração XML NF-e/NFC-e, assinatura digital, comunicação SEFAZ, consulta/cancelamento/inutilização |
|
||||
| **Variáveis** | `FISCO_PORT=8002` |
|
||||
| **Healthcheck** | `GET /health` |
|
||||
|
||||
```dockerfile
|
||||
FROM python:3.11-slim
|
||||
WORKDIR /app
|
||||
COPY server/python/fisco_service.py .
|
||||
COPY requirements-fisco.txt .
|
||||
RUN pip install -r requirements-fisco.txt
|
||||
EXPOSE 8002
|
||||
CMD ["uvicorn", "fisco_service:app", "--host", "0.0.0.0", "--port", "8002"]
|
||||
```
|
||||
|
||||
### 2.5 — `arcadia-contabil` (Motor Contábil)
|
||||
|
||||
| Item | Valor |
|
||||
|------|-------|
|
||||
| **Tecnologia** | Python 3.11 + FastAPI |
|
||||
| **Porta** | 8003 |
|
||||
| **Arquivo** | `server/python/contabil_service.py` |
|
||||
| **Banco** | Nenhum direto (recebe dados via POST) |
|
||||
| **Dependências Python** | `fastapi`, `uvicorn`, `pydantic` |
|
||||
| **Responsabilidades** | DRE, Balanço Patrimonial, Balancete, Validação de Lançamentos, SPED ECD/ECF, integração NF-e/folha |
|
||||
| **Variáveis** | `CONTABIL_PORT=8003` |
|
||||
| **Healthcheck** | `GET /health` |
|
||||
|
||||
### 2.6 — `arcadia-bi` (Motor BI/Analytics)
|
||||
|
||||
| Item | Valor |
|
||||
|------|-------|
|
||||
| **Tecnologia** | Python 3.11 + FastAPI + Pandas + NumPy |
|
||||
| **Porta** | 8004 |
|
||||
| **Arquivo** | `server/python/bi_engine.py` |
|
||||
| **Banco** | PostgreSQL (via `psycopg2`, leitura direta) |
|
||||
| **Dependências Python** | `fastapi`, `uvicorn`, `pandas`, `numpy`, `psycopg2-binary`, `pydantic` |
|
||||
| **Responsabilidades** | Exploração de schema, execução SQL, geração de dados para gráficos, Micro-BI, análise de dados |
|
||||
| **Variáveis** | `BI_PORT=8004`, `DATABASE_URL` |
|
||||
| **Healthcheck** | `GET /health` |
|
||||
|
||||
### 2.7 — `arcadia-automation` (Motor de Automação)
|
||||
|
||||
| Item | Valor |
|
||||
|------|-------|
|
||||
| **Tecnologia** | Python 3.11 + FastAPI + APScheduler |
|
||||
| **Porta** | 8005 |
|
||||
| **Arquivo** | `server/python/automation_engine.py` |
|
||||
| **Banco** | PostgreSQL (via `psycopg2`) |
|
||||
| **Dependências Python** | `fastapi`, `uvicorn`, `psycopg2-binary`, `apscheduler`, `pydantic` |
|
||||
| **Responsabilidades** | Scheduler (cron jobs), Event Bus, Workflow executor |
|
||||
| **Variáveis** | `AUTOMATION_PORT=8005`, `DATABASE_URL` |
|
||||
| **Healthcheck** | `GET /health` |
|
||||
|
||||
### 2.8 — `arcadia-communication` (Motor de Comunicação)
|
||||
|
||||
| Item | Valor |
|
||||
|------|-------|
|
||||
| **Tecnologia** | Node.js 20 + Express + TypeScript |
|
||||
| **Porta** | 8006 |
|
||||
| **Arquivo** | `server/communication/engine.ts` |
|
||||
| **Banco** | PostgreSQL (via `@neondatabase/serverless`) |
|
||||
| **Dependências Node** | `express`, `@neondatabase/serverless`, `ws` |
|
||||
| **Responsabilidades** | Unifica contatos/threads/mensagens de WhatsApp, CRM, XOS. Canais, filas, mensagens rápidas, eventos |
|
||||
| **Variáveis** | `COMMUNICATION_PORT=8006`, `DATABASE_URL` |
|
||||
| **Healthcheck** | `GET /health` |
|
||||
|
||||
### 2.9 — `arcadia-people` (Motor RH — opcional/futuro)
|
||||
|
||||
| Item | Valor |
|
||||
|------|-------|
|
||||
| **Tecnologia** | Python 3.11 + FastAPI |
|
||||
| **Porta** | 8007 (ajustar — conflito atual com BI na 8004) |
|
||||
| **Arquivo** | `server/python/people_service.py` |
|
||||
| **Banco** | Nenhum direto (cálculos puros) |
|
||||
| **Dependências Python** | `fastapi`, `uvicorn`, `pydantic` |
|
||||
| **Responsabilidades** | Cálculo INSS/IRRF, folha de pagamento, férias, rescisão, eSocial |
|
||||
| **Variáveis** | `PEOPLE_PORT=8007` |
|
||||
|
||||
---
|
||||
|
||||
## 3. Bancos de Dados (3 containers)
|
||||
|
||||
### 3.1 — `arcadia-postgres`
|
||||
|
||||
| Item | Valor |
|
||||
|------|-------|
|
||||
| **Imagem** | `postgres:16-alpine` |
|
||||
| **Porta** | 5432 |
|
||||
| **Uso** | Gateway, BI Engine, Automation Engine, Communication Engine |
|
||||
| **ORM** | Drizzle (Node.js) / psycopg2 (Python) |
|
||||
| **Schema** | ~100+ tabelas (tenants, users, valuation_*, crm_*, comm_*, xos_*, etc.) |
|
||||
| **Volume** | `/var/lib/postgresql/data` |
|
||||
|
||||
### 3.2 — `arcadia-mysql`
|
||||
|
||||
| Item | Valor |
|
||||
|------|-------|
|
||||
| **Imagem** | `mysql:8.0` |
|
||||
| **Porta** | 3306 |
|
||||
| **Uso** | Exclusivo do Plus (Laravel) |
|
||||
| **Schema** | 260+ migrações Laravel (produtos, vendas, fiscal, estoque, etc.) |
|
||||
| **Volume** | `/var/lib/mysql` |
|
||||
|
||||
### 3.3 — `arcadia-redis` (futuro/recomendado)
|
||||
|
||||
| Item | Valor |
|
||||
|------|-------|
|
||||
| **Imagem** | `redis:7-alpine` |
|
||||
| **Porta** | 6379 |
|
||||
| **Uso** | Cache de sessões, fila de jobs, pub/sub entre serviços |
|
||||
| **Recomendação** | Substituir session store do Express (atualmente PostgreSQL) e usar como broker de eventos |
|
||||
|
||||
---
|
||||
|
||||
## 4. Mapa de Comunicação entre Serviços
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ arcadia-gateway (:5000) │
|
||||
│ │
|
||||
│ /api/* → Rotas internas (Express) │
|
||||
│ /api/fisco/* → proxy → arcadia-fisco:8002 │
|
||||
│ /api/contabil/* → proxy → arcadia-contabil:8003 │
|
||||
│ /api/bi/* → proxy → arcadia-bi:8004 │
|
||||
│ /api/automation/*→ proxy → arcadia-automation:8005 │
|
||||
│ /api/comm/* → proxy → arcadia-communication:8006 │
|
||||
│ /plus/* → proxy → arcadia-plus-web:8080 │
|
||||
│ /metabase/* → proxy → metabase:8088 (se usar) │
|
||||
│ │
|
||||
│ WebSocket (Socket.IO) → Chat interno, WhatsApp real-time │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Dependências entre serviços:
|
||||
|
||||
| Serviço | Depende de | Comunicação |
|
||||
|---------|-----------|-------------|
|
||||
| Gateway | PostgreSQL, todos os motores | HTTP proxy, DB direto |
|
||||
| Plus | MySQL, Gateway (SSO) | HTTP (proxy reverso) |
|
||||
| Fisco | Nenhum banco | HTTP (recebe XMLs) |
|
||||
| Contábil | Nenhum banco | HTTP (recebe dados via POST) |
|
||||
| BI | PostgreSQL | HTTP + SQL direto no PG |
|
||||
| Automation | PostgreSQL | HTTP + SQL direto no PG |
|
||||
| Communication | PostgreSQL | HTTP + SQL direto no PG |
|
||||
| People | Nenhum banco | HTTP (cálculos puros) |
|
||||
|
||||
---
|
||||
|
||||
## 5. Docker Compose Completo para Coolify
|
||||
|
||||
```yaml
|
||||
# docker-compose.coolify.yml
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
# ═══════════════════════════════════════════
|
||||
# BANCOS DE DADOS
|
||||
# ═══════════════════════════════════════════
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: ${PGUSER:-arcadia}
|
||||
POSTGRES_PASSWORD: ${PGPASSWORD}
|
||||
POSTGRES_DB: ${PGDATABASE:-arcadia}
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${PGUSER:-arcadia}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- arcadia
|
||||
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
restart: always
|
||||
command: >
|
||||
--default-authentication-plugin=mysql_native_password
|
||||
--character-set-server=utf8mb4
|
||||
--collation-server=utf8mb4_unicode_ci
|
||||
environment:
|
||||
MYSQL_DATABASE: ${PLUS_DB_DATABASE:-controlplus}
|
||||
MYSQL_USER: ${PLUS_DB_USERNAME:-controlplus}
|
||||
MYSQL_PASSWORD: ${PLUS_DB_PASSWORD}
|
||||
MYSQL_ROOT_PASSWORD: ${PLUS_MYSQL_ROOT_PASSWORD}
|
||||
volumes:
|
||||
- mysqldata:/var/lib/mysql
|
||||
ports:
|
||||
- "3306:3306"
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- arcadia
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: always
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redisdata:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
networks:
|
||||
- arcadia
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# GATEWAY + FRONTEND (Node.js principal)
|
||||
# ═══════════════════════════════════════════
|
||||
gateway:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
restart: always
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "5000:5000"
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 5000
|
||||
DATABASE_URL: postgresql://${PGUSER:-arcadia}:${PGPASSWORD}@postgres:5432/${PGDATABASE:-arcadia}
|
||||
PGHOST: postgres
|
||||
PGUSER: ${PGUSER:-arcadia}
|
||||
PGPASSWORD: ${PGPASSWORD}
|
||||
PGDATABASE: ${PGDATABASE:-arcadia}
|
||||
SESSION_SECRET: ${SESSION_SECRET}
|
||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||
GITHUB_TOKEN: ${GITHUB_TOKEN}
|
||||
ERPNEXT_URL: ${ERPNEXT_URL:-}
|
||||
ERPNEXT_API_KEY: ${ERPNEXT_API_KEY:-}
|
||||
ERPNEXT_API_SECRET: ${ERPNEXT_API_SECRET:-}
|
||||
# URLs dos motores (comunicação interna Docker)
|
||||
FISCO_URL: http://fisco:8002
|
||||
CONTABIL_URL: http://contabil:8003
|
||||
BI_URL: http://bi:8004
|
||||
AUTOMATION_URL: http://automation:8005
|
||||
COMMUNICATION_URL: http://communication:8006
|
||||
PLUS_URL: http://plus-web:8080
|
||||
REDIS_URL: redis://redis:6379
|
||||
volumes:
|
||||
- uploads:/app/uploads
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-qO-", "http://localhost:5000/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
start_period: 40s
|
||||
networks:
|
||||
- arcadia
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# MOTORES PYTHON (FastAPI)
|
||||
# ═══════════════════════════════════════════
|
||||
fisco:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/fisco.Dockerfile
|
||||
restart: always
|
||||
ports:
|
||||
- "8002:8002"
|
||||
environment:
|
||||
FISCO_PORT: 8002
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8002/health"]
|
||||
interval: 30s
|
||||
networks:
|
||||
- arcadia
|
||||
|
||||
contabil:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/contabil.Dockerfile
|
||||
restart: always
|
||||
ports:
|
||||
- "8003:8003"
|
||||
environment:
|
||||
CONTABIL_PORT: 8003
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8003/health"]
|
||||
interval: 30s
|
||||
networks:
|
||||
- arcadia
|
||||
|
||||
bi:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/bi.Dockerfile
|
||||
restart: always
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "8004:8004"
|
||||
environment:
|
||||
BI_PORT: 8004
|
||||
DATABASE_URL: postgresql://${PGUSER:-arcadia}:${PGPASSWORD}@postgres:5432/${PGDATABASE:-arcadia}
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8004/health"]
|
||||
interval: 30s
|
||||
networks:
|
||||
- arcadia
|
||||
|
||||
automation:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/automation.Dockerfile
|
||||
restart: always
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "8005:8005"
|
||||
environment:
|
||||
AUTOMATION_PORT: 8005
|
||||
DATABASE_URL: postgresql://${PGUSER:-arcadia}:${PGPASSWORD}@postgres:5432/${PGDATABASE:-arcadia}
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8005/health"]
|
||||
interval: 30s
|
||||
networks:
|
||||
- arcadia
|
||||
|
||||
communication:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/communication.Dockerfile
|
||||
restart: always
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "8006:8006"
|
||||
environment:
|
||||
COMMUNICATION_PORT: 8006
|
||||
DATABASE_URL: postgresql://${PGUSER:-arcadia}:${PGPASSWORD}@postgres:5432/${PGDATABASE:-arcadia}
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8006/health"]
|
||||
interval: 30s
|
||||
networks:
|
||||
- arcadia
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ARCÁDIA PLUS (Laravel/PHP)
|
||||
# ═══════════════════════════════════════════
|
||||
plus-app:
|
||||
build:
|
||||
context: ./plus
|
||||
dockerfile: Dockerfile
|
||||
restart: always
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
env_file:
|
||||
- ./plus/.env
|
||||
volumes:
|
||||
- ./plus:/var/www/html:cached
|
||||
- plus-vendor:/var/www/html/vendor
|
||||
networks:
|
||||
- arcadia
|
||||
|
||||
plus-web:
|
||||
image: nginx:1.25-alpine
|
||||
restart: always
|
||||
depends_on:
|
||||
- plus-app
|
||||
ports:
|
||||
- "8080:80"
|
||||
volumes:
|
||||
- ./plus:/var/www/html:cached
|
||||
- ./plus/docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
networks:
|
||||
- arcadia
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
mysqldata:
|
||||
redisdata:
|
||||
uploads:
|
||||
plus-vendor:
|
||||
|
||||
networks:
|
||||
arcadia:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Variáveis de Ambiente (.env para Coolify)
|
||||
|
||||
```env
|
||||
# ══════════════════════════════════════
|
||||
# POSTGRESQL (Principal)
|
||||
# ══════════════════════════════════════
|
||||
PGUSER=arcadia
|
||||
PGPASSWORD=SuaSenhaSegura123!
|
||||
PGDATABASE=arcadia
|
||||
DATABASE_URL=postgresql://arcadia:SuaSenhaSegura123!@postgres:5432/arcadia
|
||||
|
||||
# ══════════════════════════════════════
|
||||
# GATEWAY (Node.js)
|
||||
# ══════════════════════════════════════
|
||||
NODE_ENV=production
|
||||
PORT=5000
|
||||
SESSION_SECRET=sua-session-secret-aqui
|
||||
|
||||
# ══════════════════════════════════════
|
||||
# INTELIGÊNCIA ARTIFICIAL
|
||||
# ══════════════════════════════════════
|
||||
OPENAI_API_KEY=sk-...
|
||||
|
||||
# ══════════════════════════════════════
|
||||
# GITHUB
|
||||
# ══════════════════════════════════════
|
||||
GITHUB_TOKEN=ghp_...
|
||||
GITHUB_OWNER=jonaspachecoometas
|
||||
GITHUB_REPO=arcadiasuite
|
||||
|
||||
# ══════════════════════════════════════
|
||||
# ERPNEXT (opcional)
|
||||
# ══════════════════════════════════════
|
||||
ERPNEXT_URL=https://seu-erpnext.com
|
||||
ERPNEXT_API_KEY=
|
||||
ERPNEXT_API_SECRET=
|
||||
|
||||
# ══════════════════════════════════════
|
||||
# MYSQL (Plus/Laravel)
|
||||
# ══════════════════════════════════════
|
||||
PLUS_DB_DATABASE=controlplus
|
||||
PLUS_DB_USERNAME=controlplus
|
||||
PLUS_DB_PASSWORD=SenhaMySQLSegura!
|
||||
PLUS_MYSQL_ROOT_PASSWORD=RootSenha123!
|
||||
|
||||
# ══════════════════════════════════════
|
||||
# REDIS
|
||||
# ══════════════════════════════════════
|
||||
REDIS_URL=redis://redis:6379
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Dockerfiles dos Motores (a criar em `docker/`)
|
||||
|
||||
### `docker/fisco.Dockerfile`
|
||||
```dockerfile
|
||||
FROM python:3.11-slim
|
||||
WORKDIR /app
|
||||
RUN pip install fastapi uvicorn pydantic cryptography lxml zeep signxml
|
||||
# nfelib se disponível: RUN pip install nfelib
|
||||
COPY server/python/fisco_service.py ./main.py
|
||||
EXPOSE 8002
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8002"]
|
||||
```
|
||||
|
||||
### `docker/contabil.Dockerfile`
|
||||
```dockerfile
|
||||
FROM python:3.11-slim
|
||||
WORKDIR /app
|
||||
RUN pip install fastapi uvicorn pydantic
|
||||
COPY server/python/contabil_service.py ./main.py
|
||||
EXPOSE 8003
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8003"]
|
||||
```
|
||||
|
||||
### `docker/bi.Dockerfile`
|
||||
```dockerfile
|
||||
FROM python:3.11-slim
|
||||
WORKDIR /app
|
||||
RUN pip install fastapi uvicorn pydantic pandas numpy psycopg2-binary
|
||||
COPY server/python/bi_engine.py ./main.py
|
||||
EXPOSE 8004
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8004"]
|
||||
```
|
||||
|
||||
### `docker/automation.Dockerfile`
|
||||
```dockerfile
|
||||
FROM python:3.11-slim
|
||||
WORKDIR /app
|
||||
RUN pip install fastapi uvicorn pydantic psycopg2-binary apscheduler
|
||||
COPY server/python/automation_engine.py ./main.py
|
||||
EXPOSE 8005
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8005"]
|
||||
```
|
||||
|
||||
### `docker/communication.Dockerfile`
|
||||
```dockerfile
|
||||
FROM node:20-alpine
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json* ./
|
||||
RUN npm install --legacy-peer-deps --production
|
||||
COPY server/communication/ ./server/communication/
|
||||
COPY shared/ ./shared/
|
||||
COPY tsconfig.json ./
|
||||
EXPOSE 8006
|
||||
CMD ["npx", "tsx", "server/communication/engine.ts"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Ordem de Deploy no Coolify
|
||||
|
||||
### Fase 1 — Infraestrutura (subir primeiro)
|
||||
1. `arcadia-postgres` — Banco principal
|
||||
2. `arcadia-mysql` — Banco do Plus
|
||||
3. `arcadia-redis` — Cache e filas
|
||||
|
||||
### Fase 2 — Motores Independentes (podem subir em paralelo)
|
||||
4. `arcadia-fisco` — Sem dependência de banco
|
||||
5. `arcadia-contabil` — Sem dependência de banco
|
||||
6. `arcadia-bi` — Depende do PostgreSQL
|
||||
7. `arcadia-automation` — Depende do PostgreSQL
|
||||
8. `arcadia-communication` — Depende do PostgreSQL
|
||||
|
||||
### Fase 3 — Aplicações Principais
|
||||
9. `arcadia-gateway` — Depende de PostgreSQL + todos os motores
|
||||
10. `arcadia-plus-app` + `arcadia-plus-web` — Depende do MySQL
|
||||
|
||||
### Fase 4 — Migrações (após tudo no ar)
|
||||
```bash
|
||||
# PostgreSQL (Drizzle)
|
||||
docker exec arcadia-gateway npx drizzle-kit push
|
||||
|
||||
# MySQL (Laravel)
|
||||
docker exec arcadia-plus-app php artisan migrate --force
|
||||
docker exec arcadia-plus-app php artisan db:seed --force
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Ajustes Necessários no Código
|
||||
|
||||
### 9.1 — Gateway: URLs dos motores precisam ser configuráveis
|
||||
|
||||
Atualmente o código usa `localhost:800X`. Para Docker, precisa usar nomes de serviço:
|
||||
|
||||
| Arquivo | Linha | De | Para |
|
||||
|---------|-------|----|------|
|
||||
| `server/contabil/routes.ts` | proxy | `localhost:8003` | `process.env.CONTABIL_URL` |
|
||||
| `server/fisco/routes.ts` | proxy | `localhost:8002` | `process.env.FISCO_URL` |
|
||||
| `server/bi/proxy.ts` | proxy | `localhost:8004` | `process.env.BI_URL` |
|
||||
| `server/python/launcher.ts` | spawn | hardcoded | Remover spawns no modo Docker |
|
||||
| `server/plus/proxy.ts` | proxy | `localhost:8080` | `process.env.PLUS_URL` |
|
||||
| `server/communication/proxy.ts` | proxy | `localhost:8006` | `process.env.COMMUNICATION_URL` |
|
||||
|
||||
### 9.2 — Desabilitar spawns de subprocessos em produção
|
||||
|
||||
O Gateway atualmente inicia os motores Python como subprocessos. No Docker cada motor roda em seu próprio container. Adicionar flag:
|
||||
|
||||
```typescript
|
||||
// server/index.ts
|
||||
if (process.env.DOCKER_MODE !== 'true') {
|
||||
// spawn python services
|
||||
startManagedServices();
|
||||
}
|
||||
```
|
||||
|
||||
### 9.3 — Conflito de portas
|
||||
|
||||
| Serviço | Porta atual | Conflito | Solução |
|
||||
|---------|------------|----------|---------|
|
||||
| People | 8004 | BI Engine | Mudar para 8007 |
|
||||
| BI Analysis | 8003 | Contábil | Mesclar no BI Engine (8004) |
|
||||
|
||||
---
|
||||
|
||||
## 10. Checklist de Migração para Coolify
|
||||
|
||||
- [ ] Criar pasta `docker/` com Dockerfiles dos motores
|
||||
- [ ] Criar `docker-compose.coolify.yml` (baseado na seção 5)
|
||||
- [ ] Criar `.env.production` com todas as variáveis
|
||||
- [ ] Ajustar proxies do Gateway para usar variáveis de ambiente
|
||||
- [ ] Adicionar flag `DOCKER_MODE=true` para desabilitar spawns
|
||||
- [ ] Resolver conflitos de porta (People → 8007)
|
||||
- [ ] Testar build de cada Dockerfile isoladamente
|
||||
- [ ] Configurar Coolify com Docker Compose
|
||||
- [ ] Rodar migrações PostgreSQL (`drizzle-kit push`)
|
||||
- [ ] Rodar migrações MySQL (`php artisan migrate`)
|
||||
- [ ] Configurar domínio e TLS no Coolify/Traefik
|
||||
- [ ] Configurar volumes persistentes (pgdata, mysqldata, uploads)
|
||||
- [ ] Testar SSO do Plus via proxy
|
||||
- [ ] Verificar WebSocket (Socket.IO) funciona via Traefik
|
||||
- [ ] Configurar healthchecks em todos os serviços
|
||||
|
||||
---
|
||||
|
||||
## 11. Resumo Visual Rápido
|
||||
|
||||
| # | Container | Porta | Tech | Banco | Prioridade |
|
||||
|---|-----------|-------|------|-------|-----------|
|
||||
| 1 | postgres | 5432 | PostgreSQL 16 | - | Essencial |
|
||||
| 2 | mysql | 3306 | MySQL 8.0 | - | Para Plus |
|
||||
| 3 | redis | 6379 | Redis 7 | - | Recomendado |
|
||||
| 4 | gateway | 5000 | Node.js 20 | PG | Essencial |
|
||||
| 5 | fisco | 8002 | Python 3.11 | - | Módulo |
|
||||
| 6 | contabil | 8003 | Python 3.11 | - | Módulo |
|
||||
| 7 | bi | 8004 | Python 3.11 | PG | Módulo |
|
||||
| 8 | automation | 8005 | Python 3.11 | PG | Módulo |
|
||||
| 9 | communication | 8006 | Node.js 20 | PG | Módulo |
|
||||
| 10 | plus-app | 9000 | PHP 8.2 FPM | MySQL | Módulo |
|
||||
| 11 | plus-web | 8080 | Nginx 1.25 | - | Módulo |
|
||||
| 12 | people | 8007 | Python 3.11 | - | Futuro |
|
||||
|
||||
**Total: 12 containers (3 bancos + 1 gateway + 8 motores)**
|
||||
|
|
@ -0,0 +1,746 @@
|
|||
# Arcádia SOE — Sistema Operacional Empresarial
|
||||
## Mapa Completo para Deploy em Servidor Dedicado
|
||||
|
||||
> Mapa da rota `/soe`: todos os arquivos do módulo, domínios de negócio,
|
||||
> padrão de motor (adapter), tabelas PostgreSQL, APIs REST e dependências
|
||||
> externas. Atualizado em: Março 2026.
|
||||
|
||||
---
|
||||
|
||||
## 1. O que é o SOE
|
||||
|
||||
O **Arcádia SOE (Sistema Operacional Empresarial)** é o **kernel central** da Arcádia Suite — o sistema que concentra todos os domínios canônicos de negócio. Enquanto apps como Varejo, Engenharia e Food Service adicionam experiências verticais, o SOE é a camada horizontal que todos compartilham.
|
||||
|
||||
**Princípio central:** O SOE **pensa e governa**; os motores (Plus, ERPNext) **executam e registram**.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ARCÁDIA SOE — VISÃO GERAL │
|
||||
│ │
|
||||
│ URL: /soe │
|
||||
│ API: /api/soe/* (alias backward-compatible → /api/erp/*) │
|
||||
│ │
|
||||
│ ┌──────── Domínios Canônicos ────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ Pessoas │ Produtos │ Vendas │ Compras │ Estoque │ │
|
||||
│ │ Fiscal │ Financ. │ CRM │ Projetos │ Qualidade │ │
|
||||
│ │ Governa. │ RH │ │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──────── Motores (Adapters) ────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ motor = "plus" → Arcádia Plus (Laravel :8080) │ │
|
||||
│ │ motor = "erpnext" → ERPNext/Frappe (externo) │ │
|
||||
│ │ motor = nativo → PostgreSQL local (default fallback) │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──────── Apps que estendem o SOE ───────────────────────────┐ │
|
||||
│ │ Varejo (Retail) │ Engenharia │ Food Service │ │
|
||||
│ │ Assistência Tec │ Educação │ (outros segmentos) │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Arquivos do Módulo SOE — Lista Completa para Export
|
||||
|
||||
### 2.1 — Frontend (React/TypeScript)
|
||||
|
||||
| Arquivo | Função | Linhas |
|
||||
|---------|--------|--------|
|
||||
| `client/src/pages/SOE.tsx` | **Página principal** do SOE — 9 abas, CRUD completo | 2.414 |
|
||||
| `client/src/pages/ERP.tsx` | Legacy ERP — mantido para referência histórica | 2.197 |
|
||||
| `client/src/contexts/SoeMotorContext.tsx` | **Context do Motor** — gerencia plus/erpnext via localStorage | 63 |
|
||||
| `client/src/contexts/ErpProfileContext.tsx` | Alias legacy do SoeMotorContext (backward compat.) | 52 |
|
||||
|
||||
### 2.2 — Backend (Node.js/TypeScript)
|
||||
|
||||
| Arquivo | Função | Linhas |
|
||||
|---------|--------|--------|
|
||||
| `server/erp/routes.ts` | **Rotas SOE core** — /api/erp/* (= /api/soe/*) | 1.343 |
|
||||
| `server/erp/index.ts` | Setup do módulo + alias /api/soe → /api/erp | 210 |
|
||||
| `server/retail/routes.ts` | **Rotas do Varejo** — /api/retail/* | 5.218 |
|
||||
| `server/retail/index.ts` | Setup do módulo retail | — |
|
||||
| `server/retail/sync-service.ts` | Sincronização retail ↔ Plus | — |
|
||||
| `server/retail/plus-sync.ts` | Ponte retail → Arcádia Plus | — |
|
||||
| `server/plus/client.ts` | **Cliente HTTP** para Arcádia Plus (:8080) | 376 |
|
||||
| `server/plus/routes.ts` | Rotas de integração Plus | — |
|
||||
| `server/plus/launcher.ts` | Iniciador do serviço Plus | — |
|
||||
| `server/plus/proxy.ts` | Proxy transparente → Plus (:8080) | — |
|
||||
| `server/plus/sso.ts` | Single Sign-On entre SOE e Plus | — |
|
||||
| `server/erpnext/routes.ts` | Rotas de integração ERPNext | — |
|
||||
| `server/erpnext/index.ts` | Setup do módulo ERPNext | — |
|
||||
|
||||
### 2.3 — Schema PostgreSQL (Drizzle ORM)
|
||||
|
||||
| Arquivo | Tabelas relacionadas ao SOE |
|
||||
|---------|----------------------------|
|
||||
| `shared/schema.ts` (L348) | `erp_connections` |
|
||||
| `shared/schema.ts` (L3303) | `erp_segments`, `erp_config` |
|
||||
| `shared/schema.ts` (L3367–3517) | `customers`, `suppliers`, `products`, `sales_orders`, `sales_order_items`, `purchase_orders`, `purchase_order_items` |
|
||||
| `shared/schema.ts` (L5082–5617) | Tabelas retail (ver seção 5) |
|
||||
| `shared/schema.ts` (L6204) | `persons`, `person_roles` |
|
||||
| `shared/schema.ts` (L4372) | `fin_payment_methods`, `fin_payment_plans` |
|
||||
|
||||
---
|
||||
|
||||
## 3. Arquitetura de Arquivos
|
||||
|
||||
```
|
||||
workspace/
|
||||
│
|
||||
├── client/src/
|
||||
│ ├── pages/
|
||||
│ │ ├── SOE.tsx ← Página principal (/soe)
|
||||
│ │ └── ERP.tsx ← Legacy (mantido)
|
||||
│ │
|
||||
│ └── contexts/
|
||||
│ ├── SoeMotorContext.tsx ← Motor context (plus | erpnext)
|
||||
│ └── ErpProfileContext.tsx ← Alias backward-compat
|
||||
│
|
||||
├── server/
|
||||
│ ├── erp/
|
||||
│ │ ├── routes.ts ← /api/erp/* (= /api/soe/*)
|
||||
│ │ └── index.ts ← Setup + alias soe→erp
|
||||
│ │
|
||||
│ ├── retail/
|
||||
│ │ ├── routes.ts ← /api/retail/*
|
||||
│ │ ├── index.ts
|
||||
│ │ ├── sync-service.ts
|
||||
│ │ └── plus-sync.ts
|
||||
│ │
|
||||
│ ├── plus/
|
||||
│ │ ├── client.ts ← HTTP client → Plus :8080
|
||||
│ │ ├── routes.ts
|
||||
│ │ ├── proxy.ts
|
||||
│ │ ├── launcher.ts
|
||||
│ │ └── sso.ts
|
||||
│ │
|
||||
│ └── erpnext/
|
||||
│ ├── routes.ts
|
||||
│ └── index.ts
|
||||
│
|
||||
└── shared/
|
||||
└── schema.ts ← Tabelas SOE + Retail
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Motor de Execução (Adapter Pattern)
|
||||
|
||||
O SOE usa um **padrão de adapter** para rotear operações para o motor correto:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ SOEOMOTOR — ADAPTER PATTERN │
|
||||
│ │
|
||||
│ SoeMotorContext.tsx │
|
||||
│ │
|
||||
│ export type SoeMotor = "plus" | "erpnext" │
|
||||
│ │
|
||||
│ Persistência: localStorage["arcadia_soe_motor"] │
|
||||
│ │
|
||||
│ getApiUrl(localPath, plusPath, erpnextPath?) │
|
||||
│ │
|
||||
│ ┌──────── motor = "plus" ────────────────────────────────────┐ │
|
||||
│ │ → /plus/api{plusPath} │ │
|
||||
│ │ → Passa pelo proxy transparente do Plus │ │
|
||||
│ │ → Arcádia Plus (Laravel) em :8080 │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──────── motor = "erpnext" ─────────────────────────────────┐ │
|
||||
│ │ → erpnextPath ou localPath │ │
|
||||
│ │ → ERPNext API (ERPNEXT_URL env var) │ │
|
||||
│ │ → Autenticado via ERPNEXT_API_KEY + ERPNEXT_API_SECRET │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──────── motor = nativo (fallback) ─────────────────────────┐ │
|
||||
│ │ → localPath (/api/erp/*) │ │
|
||||
│ │ → PostgreSQL local via Drizzle ORM │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Seleção na UI: SOE.tsx → Config tab → Dropdown de motor │
|
||||
│ Plus ativo: isERPNextConnected verifica /api/erpnext/connections │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Domínios e Abas do SOE
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ SOE.tsx — 9 ABAS PRINCIPAIS │
|
||||
│ │
|
||||
│ ┌── Tab: dashboard ──────────────────────────────────────────┐ │
|
||||
│ │ • Stats: clientes, produtos, pedidos, total vendas │ │
|
||||
│ │ • Cards com indicadores-chave do negócio │ │
|
||||
│ │ • Acesso rápido às seções │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── Tab: persons (Pessoas) ───────────────────────────────────┐ │
|
||||
│ │ • Clientes, Fornecedores, Funcionários, Técnicos, Parceiros│ │
|
||||
│ │ • Busca + filtro por papel (role) │ │
|
||||
│ │ • Campos: nome, CPF/CNPJ, email, telefone, endereço │ │
|
||||
│ │ • Tabelas: persons + person_roles │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── Tab: products (Produtos) ─────────────────────────────────┐ │
|
||||
│ │ • Cadastro de produtos/serviços │ │
|
||||
│ │ • Rastreamento: IMEI, Número de Série, sem rastreamento │ │
|
||||
│ │ • Grupos de produto, unidades (UN/KG/M/L/CX/PC) │ │
|
||||
│ │ • Estoque por produto, devices batch import │ │
|
||||
│ │ • Tabela: products + mobile_devices │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── Tab: sales (Vendas) ──────────────────────────────────────┐ │
|
||||
│ │ • Pedidos de venda │ │
|
||||
│ │ • Status: draft → confirmed → delivered │ │
|
||||
│ │ • Geração de NF-e a partir do pedido │ │
|
||||
│ │ • Formas de pagamento: Dinheiro/Cartão/PIX/Boleto/Prazo │ │
|
||||
│ │ • Tabelas: sales_orders + sales_order_items │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── Tab: purchases (Compras) ─────────────────────────────────┐ │
|
||||
│ │ • Pedidos de compra │ │
|
||||
│ │ • Associados a fornecedores │ │
|
||||
│ │ • Tabelas: purchase_orders + purchase_order_items │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── Tab: financial (Financeiro) ──────────────────────────────┐ │
|
||||
│ │ • Visão financeira consolidada │ │
|
||||
│ │ • Planos de pagamento e parcelamento │ │
|
||||
│ │ • Tabelas: fin_payment_methods + fin_payment_plans │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── Tab: crm ─────────────────────────────────────────────────┐ │
|
||||
│ │ • Módulo CRM inline no SOE │ │
|
||||
│ │ • Integrado via useTenantFeatures (feature: crm) │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── Tab: sync (Sincronização) ────────────────────────────────┐ │
|
||||
│ │ • Status dos motores (Plus/ERPNext) │ │
|
||||
│ │ • Módulos ativos/inativos do tenant │ │
|
||||
│ │ • Botões de sync manual por entidade │ │
|
||||
│ │ • Indica se Plus está online (:8080) │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌── Tab: config (Configuração) ───────────────────────────────┐ │
|
||||
│ │ • Dados da empresa: razão social, CNPJ, regime tributário │ │
|
||||
│ │ • Inscrição estadual/municipal │ │
|
||||
│ │ • Segmento de negócio │ │
|
||||
│ │ • Seletor de motor: Plus ou ERPNext │ │
|
||||
│ │ • Tabela: erp_config + erp_segments │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Tabelas PostgreSQL — SOE Core
|
||||
|
||||
### 6.1 — erp_connections (Conexões com ERPs externos)
|
||||
|
||||
```
|
||||
erp_connections
|
||||
├── id serial PK
|
||||
├── userId FK → users.id
|
||||
├── erpType "erpnext" | "plus" | "omie" | etc.
|
||||
├── name Nome da conexão
|
||||
├── url URL do ERP
|
||||
├── apiKey Chave de API
|
||||
├── apiSecret Secret
|
||||
├── isActive boolean
|
||||
└── createdAt, updatedAt
|
||||
```
|
||||
|
||||
### 6.2 — erp_segments (Segmentos de Negócio)
|
||||
|
||||
```
|
||||
erp_segments
|
||||
├── id serial PK
|
||||
├── name Nome (Varejo, Serviços, Indústria, etc.)
|
||||
├── description Descrição
|
||||
├── icon Ícone
|
||||
└── createdAt
|
||||
```
|
||||
|
||||
### 6.3 — erp_config (Configuração da Empresa)
|
||||
|
||||
```
|
||||
erp_config
|
||||
├── id serial PK
|
||||
├── userId FK → users.id
|
||||
├── companyName Razão Social
|
||||
├── tradeName Nome Fantasia
|
||||
├── taxId CNPJ
|
||||
├── taxRegime simples | lucro_presumido | lucro_real
|
||||
├── stateRegistration Inscrição Estadual
|
||||
├── cityRegistration Inscrição Municipal
|
||||
├── segmentId FK → erp_segments.id
|
||||
├── address Endereço completo
|
||||
└── createdAt, updatedAt
|
||||
```
|
||||
|
||||
### 6.4 — customers (Clientes)
|
||||
|
||||
```
|
||||
customers
|
||||
├── id serial PK
|
||||
├── userId FK → users.id
|
||||
├── code Código interno
|
||||
├── name Nome
|
||||
├── email E-mail
|
||||
├── phone Telefone
|
||||
├── document CPF/CNPJ
|
||||
├── type company | individual
|
||||
├── address Endereço
|
||||
└── createdAt, updatedAt
|
||||
```
|
||||
|
||||
### 6.5 — suppliers (Fornecedores)
|
||||
|
||||
```
|
||||
suppliers
|
||||
├── id serial PK
|
||||
├── userId FK → users.id
|
||||
├── code Código interno
|
||||
├── name Nome/Razão Social
|
||||
├── email, phone
|
||||
├── document CNPJ/CPF
|
||||
└── createdAt, updatedAt
|
||||
```
|
||||
|
||||
### 6.6 — products (Produtos)
|
||||
|
||||
```
|
||||
products
|
||||
├── id serial PK
|
||||
├── userId FK → users.id
|
||||
├── code Código/SKU
|
||||
├── name Nome do produto
|
||||
├── description
|
||||
├── price Decimal(10,2)
|
||||
├── costPrice Preço de custo
|
||||
├── unit UN | KG | M | L | CX | PC
|
||||
├── stock Estoque atual
|
||||
├── minStock Estoque mínimo
|
||||
├── groupId Grupo de produto
|
||||
├── requiresSerialTracking boolean
|
||||
├── trackingType none | imei | serial
|
||||
└── createdAt, updatedAt
|
||||
```
|
||||
|
||||
### 6.7 — sales_orders + sales_order_items
|
||||
|
||||
```
|
||||
sales_orders
|
||||
├── id serial PK
|
||||
├── userId FK → users.id
|
||||
├── customerId FK → customers.id
|
||||
├── orderNumber Número do pedido
|
||||
├── status draft | confirmed | delivered | cancelled
|
||||
├── total Decimal(10,2)
|
||||
├── paymentMethod dinheiro | cartao_credito | pix | boleto | prazo
|
||||
└── createdAt, updatedAt
|
||||
|
||||
sales_order_items
|
||||
├── id serial PK
|
||||
├── orderId FK → sales_orders.id
|
||||
├── productId FK → products.id
|
||||
├── quantity integer
|
||||
├── unitPrice Decimal(10,2)
|
||||
└── total Decimal(10,2)
|
||||
```
|
||||
|
||||
### 6.8 — purchase_orders + purchase_order_items
|
||||
|
||||
```
|
||||
purchase_orders
|
||||
├── id serial PK
|
||||
├── userId FK → users.id
|
||||
├── supplierId FK → suppliers.id
|
||||
├── orderNumber
|
||||
├── status draft | confirmed | received | cancelled
|
||||
├── total Decimal(10,2)
|
||||
└── createdAt, updatedAt
|
||||
|
||||
purchase_order_items
|
||||
├── id serial PK
|
||||
├── orderId FK → purchase_orders.id
|
||||
├── productId FK → products.id
|
||||
├── quantity, unitPrice, total
|
||||
```
|
||||
|
||||
### 6.9 — persons + person_roles (Cadastro Unificado)
|
||||
|
||||
```
|
||||
persons
|
||||
├── id serial PK
|
||||
├── tenantId FK → tenants.id
|
||||
├── fullName
|
||||
├── cpfCnpj
|
||||
├── email, phone
|
||||
├── address, city, state, zipCode
|
||||
├── type company | individual
|
||||
└── notes, createdAt, updatedAt
|
||||
|
||||
person_roles
|
||||
├── id serial PK
|
||||
├── personId FK → persons.id
|
||||
├── role customer | supplier | employee | technician | partner
|
||||
└── isActive
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Tabelas PostgreSQL — Módulo Varejo (Retail)
|
||||
|
||||
### 7.1 — Estrutura Operacional
|
||||
|
||||
```
|
||||
retail_stores → Lojas físicas
|
||||
retail_warehouses → Depósitos/Almoxarifados
|
||||
retail_payment_methods → Formas de pagamento
|
||||
retail_sellers → Vendedores
|
||||
retail_commission_plans → Planos de comissão
|
||||
retail_seller_goals → Metas por vendedor
|
||||
retail_store_goals → Metas por loja
|
||||
```
|
||||
|
||||
### 7.2 — Estoque
|
||||
|
||||
```
|
||||
stock_transfers → Transferências entre depósitos
|
||||
stock_transfer_items → Itens da transferência
|
||||
```
|
||||
|
||||
### 7.3 — Dispositivos Móveis (Eletrônicos)
|
||||
|
||||
```
|
||||
mobile_devices
|
||||
├── id serial PK
|
||||
├── productId FK → products.id
|
||||
├── imei varchar (único)
|
||||
├── imei2 varchar (chip duplo)
|
||||
├── serial Número de série
|
||||
├── status em_estoque | vendido | locado | devolucao | avaliacao
|
||||
├── color, storage, ram
|
||||
├── purchasePrice, salePrice
|
||||
└── supplierId, createdAt
|
||||
|
||||
device_evaluations → Avaliação de aparelhos usados
|
||||
device_history → Histórico completo do dispositivo
|
||||
```
|
||||
|
||||
### 7.4 — PDV (Ponto de Venda)
|
||||
|
||||
```
|
||||
pos_sessions → Sessões de caixa (abertura/fechamento)
|
||||
pos_sales → Vendas PDV
|
||||
pos_sale_items → Itens das vendas PDV
|
||||
```
|
||||
|
||||
### 7.5 — Planos de Pagamento
|
||||
|
||||
```
|
||||
payment_plans → Planos parcelados
|
||||
payment_plan_installments → Parcelas
|
||||
lease_payments → Pagamentos de locação
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. API REST Completa (/api/soe/* = /api/erp/*)
|
||||
|
||||
> O alias `/api/soe/*` é convertido automaticamente para `/api/erp/*`
|
||||
> no middleware do `server/erp/index.ts`.
|
||||
|
||||
### 8.1 — Conexões ERP
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/soe/connections` | Lista conexões do usuário |
|
||||
| POST | `/api/soe/connections` | Cria nova conexão |
|
||||
| POST | `/api/soe/connections/:id/test` | Testa conectividade |
|
||||
| DELETE | `/api/soe/connections/:id` | Remove conexão |
|
||||
|
||||
### 8.2 — Tarefas ERP (Sync Jobs)
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/soe/tasks` | Lista tarefas de sync |
|
||||
| POST | `/api/soe/tasks` | Cria nova tarefa |
|
||||
| POST | `/api/soe/tasks/:id/execute` | Executa tarefa manualmente |
|
||||
| DELETE | `/api/soe/tasks/:id` | Remove tarefa |
|
||||
| GET | `/api/soe/tasks/:id/executions` | Histórico de execuções |
|
||||
|
||||
### 8.3 — Clientes
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/soe/customers` | Lista clientes (paginado) |
|
||||
| POST | `/api/soe/customers` | Cria cliente |
|
||||
| PUT | `/api/soe/customers/:id` | Atualiza cliente |
|
||||
| DELETE | `/api/soe/customers/:id` | Exclui cliente |
|
||||
|
||||
### 8.4 — Fornecedores
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/soe/suppliers` | Lista fornecedores |
|
||||
| POST | `/api/soe/suppliers` | Cria fornecedor |
|
||||
| PUT | `/api/soe/suppliers/:id` | Atualiza |
|
||||
| DELETE | `/api/soe/suppliers/:id` | Exclui |
|
||||
|
||||
### 8.5 — Pessoas (Cadastro Unificado)
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/soe/persons` | Lista com filtro por role/search |
|
||||
| GET | `/api/soe/persons/:id` | Detalhe + roles |
|
||||
| POST | `/api/soe/persons` | Cria pessoa + roles |
|
||||
| PUT | `/api/soe/persons/:id` | Atualiza pessoa + roles |
|
||||
| DELETE | `/api/soe/persons/:id` | Exclui pessoa (cascade roles) |
|
||||
|
||||
### 8.6 — Produtos
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/soe/products` | Lista produtos |
|
||||
| POST | `/api/soe/products` | Cria produto |
|
||||
| PUT | `/api/soe/products/:id` | Atualiza |
|
||||
| DELETE | `/api/soe/products/:id` | Exclui |
|
||||
| GET | `/api/soe/products/:id/devices` | Dispositivos do produto |
|
||||
| POST | `/api/soe/products/:id/devices/batch` | Import batch de IMEIs |
|
||||
| GET | `/api/soe/products/:id/stock-count` | Contagem de estoque |
|
||||
|
||||
### 8.7 — Pedidos de Venda
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/soe/sales-orders` | Lista pedidos |
|
||||
| POST | `/api/soe/sales-orders` | Cria pedido (com itens) |
|
||||
| POST | `/api/soe/sales-orders/:id/confirm` | Confirma pedido |
|
||||
| POST | `/api/soe/sales-orders/:id/generate-nfe` | Gera NF-e |
|
||||
| DELETE | `/api/soe/sales-orders/:id` | Cancela/Exclui |
|
||||
|
||||
### 8.8 — Pedidos de Compra
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/soe/purchase-orders` | Lista pedidos |
|
||||
| POST | `/api/soe/purchase-orders` | Cria pedido |
|
||||
| DELETE | `/api/soe/purchase-orders/:id` | Exclui |
|
||||
|
||||
### 8.9 — Dados e Configuração
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/soe/stats` | Dashboard — totais de clientes/produtos/pedidos/receita |
|
||||
| GET | `/api/soe/data/:connectionId/:dataType` | Dados via conexão ERP |
|
||||
| GET | `/api/soe/segments` | Lista segmentos |
|
||||
| POST | `/api/soe/segments` | Cria segmento |
|
||||
| POST | `/api/soe/segments/seed` | Popula segmentos padrão |
|
||||
| GET | `/api/soe/config` | Configuração da empresa |
|
||||
| POST | `/api/soe/config` | Salva configuração |
|
||||
|
||||
### 8.10 — Módulos do Tenant
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/api/soe/tenant/modules` | Módulos ativos do tenant atual |
|
||||
| PUT | `/api/soe/tenant/modules` | Ativa/desativa módulos |
|
||||
|
||||
---
|
||||
|
||||
## 9. API REST — Módulo Varejo (/api/retail/*)
|
||||
|
||||
### 9.1 — Estrutura Operacional
|
||||
|
||||
| Módulo | Endpoints | Operações |
|
||||
|--------|-----------|-----------|
|
||||
| Activity Feed | `/activity-feed` | GET, mark-read |
|
||||
| Payment Methods | `/payment-methods` | CRUD |
|
||||
| Sellers | `/sellers` | CRUD |
|
||||
| Commission Plans | `/commission-plans` | CRUD |
|
||||
| Price Tables | `/price-tables` | CRUD |
|
||||
| Promotions | `/promotions` | CRUD |
|
||||
| Product Types | `/product-types` | CRUD + detail |
|
||||
| Stores | `/stores` | CRU |
|
||||
| Warehouses | `/warehouses` | CRUD |
|
||||
|
||||
### 9.2 — Estoque
|
||||
|
||||
| Módulo | Endpoints | Operações |
|
||||
|--------|-----------|-----------|
|
||||
| Warehouse Stock | `/warehouse-stock` | GET list + summary |
|
||||
| Stock Movements | `/stock-movements` | GET + POST |
|
||||
| Product Serials | `/product-serials` | CRUD |
|
||||
| Stock Transfers | `/stock-transfers` | CRUD + status |
|
||||
| Stock Alerts | `/stock-alerts` | GET |
|
||||
| Inventories | `/inventories` | CRUD + count + apply |
|
||||
|
||||
### 9.3 — Dispositivos (Mobile/Eletrônicos)
|
||||
|
||||
| Módulo | Endpoints | Operações |
|
||||
|--------|-----------|-----------|
|
||||
| Devices | `/devices` | CRUD + IMEI lookup |
|
||||
| Evaluations | `/evaluations` | CRUD + approve + reject |
|
||||
|
||||
### 9.4 — Serviços
|
||||
|
||||
| Módulo | Endpoints | Operações |
|
||||
|--------|-----------|-----------|
|
||||
| Service Orders | `/service-orders` | CRUD + complete-preparation + items |
|
||||
| Warranties | `/warranties` | CRUD + check IMEI + claim |
|
||||
|
||||
### 9.5 — PDV
|
||||
|
||||
| Módulo | Endpoints | Operações |
|
||||
|--------|-----------|-----------|
|
||||
| POS Sessions | `/pos-sessions` | GET + open + close |
|
||||
| Cash Movements | `/cash-movements` | GET + POST |
|
||||
|
||||
### 9.6 — Relatórios
|
||||
|
||||
| Endpoint | Descrição |
|
||||
|----------|-----------|
|
||||
| `/reports/os-by-status` | OS agrupadas por status |
|
||||
| `/reports/os-by-technician` | OS agrupadas por técnico |
|
||||
|
||||
**Total: ~120 endpoints no módulo SOE (Core + Retail)**
|
||||
|
||||
---
|
||||
|
||||
## 10. Dependências Externas do SOE
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ DEPENDÊNCIAS DO SOE │
|
||||
│ │
|
||||
│ ┌──── Arcádia Plus (Laravel) ────────────────────────────────┐ │
|
||||
│ │ Host: :8080 (mesmo servidor) ou externo │ │
|
||||
│ │ Env: PLUS_URL (se externo) │ │
|
||||
│ │ Proxy: /plus/* → :8080 │ │
|
||||
│ │ SSO: token compartilhado via plus/sso.ts │ │
|
||||
│ │ Sync: retail/plus-sync.ts + retail/sync-service.ts │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──── ERPNext/Frappe ─────────────────────────────────────────┐ │
|
||||
│ │ Host: ERPNEXT_URL (env var no servidor) │ │
|
||||
│ │ Auth: ERPNEXT_API_KEY + ERPNEXT_API_SECRET │ │
|
||||
│ │ Entidades: Customer, Supplier, Item, Sales Order │ │
|
||||
│ │ Rotas: server/erpnext/routes.ts → /api/erpnext/* │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──── Arcádia Fisco (:8002) ──────────────────────────────────┐ │
|
||||
│ │ Geração de NF-e a partir do pedido de venda │ │
|
||||
│ │ POST /api/soe/sales-orders/:id/generate-nfe │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──── PostgreSQL ─────────────────────────────────────────────┐ │
|
||||
│ │ DATABASE_URL env var │ │
|
||||
│ │ Motor padrão (nativo) sem Plus/ERPNext │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Variáveis de Ambiente Necessárias para Deploy
|
||||
|
||||
```bash
|
||||
# PostgreSQL (obrigatório)
|
||||
DATABASE_URL=postgresql://user:pass@host:5432/arcadia
|
||||
|
||||
# ERPNext (opcional — apenas se motor = "erpnext")
|
||||
ERPNEXT_URL=https://seu-erpnext.com
|
||||
ERPNEXT_API_KEY=sua-chave
|
||||
ERPNEXT_API_SECRET=seu-secret
|
||||
|
||||
# Arcádia Plus (opcional — apenas se motor = "plus")
|
||||
PLUS_URL=http://localhost:8080 # ou URL externa do Plus
|
||||
|
||||
# Fisco (para geração de NF-e via SOE)
|
||||
FISCO_URL=http://localhost:8002 # ou URL externa do Fisco
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Checklist de Export para Deploy Manual
|
||||
|
||||
### Frontend
|
||||
- [ ] `client/src/pages/SOE.tsx`
|
||||
- [ ] `client/src/pages/ERP.tsx`
|
||||
- [ ] `client/src/contexts/SoeMotorContext.tsx`
|
||||
- [ ] `client/src/contexts/ErpProfileContext.tsx`
|
||||
|
||||
### Backend
|
||||
- [ ] `server/erp/routes.ts`
|
||||
- [ ] `server/erp/index.ts`
|
||||
- [ ] `server/retail/routes.ts`
|
||||
- [ ] `server/retail/index.ts`
|
||||
- [ ] `server/retail/sync-service.ts`
|
||||
- [ ] `server/retail/plus-sync.ts`
|
||||
- [ ] `server/plus/client.ts`
|
||||
- [ ] `server/plus/routes.ts`
|
||||
- [ ] `server/plus/proxy.ts`
|
||||
- [ ] `server/plus/launcher.ts`
|
||||
- [ ] `server/plus/sso.ts`
|
||||
- [ ] `server/erpnext/routes.ts`
|
||||
- [ ] `server/erpnext/index.ts`
|
||||
|
||||
### Schema e Banco
|
||||
- [ ] `shared/schema.ts` (tabelas: L348, L3303–3517, L5082–5617, L6204)
|
||||
- [ ] Rodar `drizzle-kit push` no servidor para criar as tabelas
|
||||
- [ ] Verificar variável `DATABASE_URL` no servidor
|
||||
|
||||
### Configuração do Servidor
|
||||
- [ ] `.env` com `DATABASE_URL` (obrigatório)
|
||||
- [ ] `.env` com `ERPNEXT_*` (se usar motor ERPNext)
|
||||
- [ ] `.env` com `PLUS_URL` (se usar motor Plus)
|
||||
- [ ] Confirmar que `:8080` (Plus) está acessível se necessário
|
||||
|
||||
---
|
||||
|
||||
## 13. Resumo Visual do Módulo
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ARCÁDIA SOE — STACK COMPLETO │
|
||||
│ │
|
||||
│ URL /soe → SOE.tsx (2.414L) │
|
||||
│ │
|
||||
│ 9 Abas: Dashboard │ Pessoas │ Produtos │ Vendas │ Compras │
|
||||
│ Financeiro │ CRM │ Sync │ Config │
|
||||
│ │
|
||||
│ ┌─── Motor (Adapter) ────────────────────────────────────────┐ │
|
||||
│ │ localStorage["arcadia_soe_motor"] = "plus" | "erpnext" │ │
|
||||
│ │ │ │
|
||||
│ │ plus → /plus/api/* → Arcádia Plus :8080 (Laravel) │ │
|
||||
│ │ erpnext → ERPNext API → ERPNEXT_URL externo │ │
|
||||
│ │ nativo → /api/soe/* → PostgreSQL local (Drizzle) │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── Backend ────────────────────────────────────────────────┐ │
|
||||
│ │ /api/soe/* → /api/erp/* (alias automático) │ │
|
||||
│ │ server/erp/routes.ts (1.343L) → 35+ endpoints core │ │
|
||||
│ │ server/retail/routes.ts (5.218L) → 85+ endpoints varejo │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── Banco de Dados ─────────────────────────────────────────┐ │
|
||||
│ │ Core: customers, suppliers, products, sales_orders, │ │
|
||||
│ │ purchase_orders, persons, erp_config, erp_segments │ │
|
||||
│ │ │ │
|
||||
│ │ Retail: retail_stores, retail_warehouses, mobile_devices, │ │
|
||||
│ │ device_evaluations, pos_sessions, pos_sales, │ │
|
||||
│ │ stock_transfers, payment_plans (20+ tabelas) │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─── Integrações ────────────────────────────────────────────┐ │
|
||||
│ │ Arcádia Plus :8080 │ ERPNext/Frappe │ Fisco :8002 │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
|
@ -0,0 +1,812 @@
|
|||
# Arcádia Tenants — Mapa Completo da Gestão Multi-Tenant
|
||||
|
||||
> Mapa da arquitetura multi-tenant da Arcádia Suite: hierarquia
|
||||
> Master/Parceiro/Cliente, sistema de planos, ativação de módulos,
|
||||
> empresas (Matriz/Filiais), comissões de parceiros, controle de acesso,
|
||||
> e middleware de isolamento.
|
||||
> Atualizado em: Março 2026
|
||||
|
||||
---
|
||||
|
||||
## 1. Visão Geral
|
||||
|
||||
A Arcádia Suite opera em modelo **multi-tenant hierárquico** com três níveis. Cada tenant possui um plano que define quais módulos da plataforma estão habilitados, e pode conter múltiplas empresas (CNPJ) para operações fiscais.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ HIERARQUIA DE TENANTS │
|
||||
│ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ MASTER │ ← Operador da plataforma │
|
||||
│ │ │ ← Vê tudo, controla tudo │
|
||||
│ │ tenantType: │ ← Pode criar parceiros │
|
||||
│ │ "master" │ ← Pode excluir tenants │
|
||||
│ └──────┬───────┘ │
|
||||
│ │ │
|
||||
│ ┌────────────┼────────────┐ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ PARCEIRO A │ │ PARCEIRO B │ │ PARCEIRO C │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ tenantType: │ │ Consultorias │ │ Integradores │ │
|
||||
│ │ "partner" │ │ Revendas │ │ Franquias │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ partnerCode: │ │ comissão: % │ │ plano: │ │
|
||||
│ │ "PARC-001" │ │ sobre clientes│ │partner_pro │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
||||
│ │ │ │ │
|
||||
│ ┌────┴────┐ ┌───┴───┐ ┌───┴───┐ │
|
||||
│ ▼ ▼ ▼ ▼ ▼ ▼ │
|
||||
│ ┌────────┐┌────────┐┌────────┐┌────────┐┌────────┐┌────────┐ │
|
||||
│ │Cliente1││Cliente2││Cliente3││Cliente4││Cliente5││Cliente6│ │
|
||||
│ │ ││ ││ ││ ││ ││ │ │
|
||||
│ │tenant ││tenant ││tenant ││tenant ││tenant ││tenant │ │
|
||||
│ │Type: ││Type: ││Type: ││Type: ││Type: ││Type: │ │
|
||||
│ │"client"││"client"││"client"││"client"││"client"││"client"│ │
|
||||
│ │ ││ ││ ││ ││ ││ │ │
|
||||
│ │parent: ││parent: ││parent: ││parent: ││parent: ││parent: │ │
|
||||
│ │Parc.A ││Parc.A ││Parc.B ││Parc.B ││Parc.C ││Parc.C │ │
|
||||
│ │ ││ ││ ││ ││ ││ │ │
|
||||
│ │Plano: ││Plano: ││Plano: ││Plano: ││Plano: ││Plano: │ │
|
||||
│ │starter ││pro ││free ││enterpr.││starter ││pro │ │
|
||||
│ └────────┘└────────┘└────────┘└────────┘└────────┘└────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Visibilidade por tipo:
|
||||
|
||||
| Tipo | Vê | Cria | Exclui |
|
||||
|------|-----|------|--------|
|
||||
| **Master** | Todos os tenants | Parceiros e Clientes | Qualquer (exceto master) |
|
||||
| **Partner** | Seu tenant + seus clientes | Apenas clientes vinculados | Nenhum |
|
||||
| **Client** | Apenas seu próprio tenant | Nenhum | Nenhum |
|
||||
|
||||
---
|
||||
|
||||
## 2. Banco de Dados — Tabelas de Tenant
|
||||
|
||||
### 2.1 — tenants (Tabela Principal)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ tenants │
|
||||
│ Tabela: "tenants" — shared/schema.ts:849 │
|
||||
│ │
|
||||
│ ┌─────────── Identificação ──────────────────┐ │
|
||||
│ │ id serial PK │ │
|
||||
│ │ name text NOT NULL │ │
|
||||
│ │ slug text UNIQUE │ │
|
||||
│ │ email text │ │
|
||||
│ │ phone text │ │
|
||||
│ │ logoUrl text │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Hierarquia ─────────────────────┐ │
|
||||
│ │ tenantType "master" | "partner" | │ │
|
||||
│ │ "client" │ │
|
||||
│ │ parentTenantId integer (self-ref FK) │ │
|
||||
│ │ partnerCode text (código único parceiro) │ │
|
||||
│ │ commissionRate numeric(5,2) (% comissão) │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Plano e Limites ────────────────┐ │
|
||||
│ │ plan "free" | "starter" | "pro" │ │
|
||||
│ │ | "enterprise" | │ │
|
||||
│ │ "partner_starter" | │ │
|
||||
│ │ "partner_pro" │ │
|
||||
│ │ status "active" | "trial" | │ │
|
||||
│ │ "suspended" | "cancelled" │ │
|
||||
│ │ maxUsers integer (default 5) │ │
|
||||
│ │ maxStorageMb integer (default 1000) │ │
|
||||
│ │ features JSONB → TenantFeatures │ ← Módulos │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Billing ────────────────────────┐ │
|
||||
│ │ billingEmail text │ │
|
||||
│ │ trialEndsAt timestamp │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Dados Empresariais ─────────────┐ │
|
||||
│ │ cnpj text │ │
|
||||
│ │ tradeName text │ │
|
||||
│ │ address text │ │
|
||||
│ │ city, state text │ │
|
||||
│ │ segment text │ │
|
||||
│ │ notes, source text │ │
|
||||
│ │ commercialContact / commercialPhone │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Metadata ───────────────────────┐ │
|
||||
│ │ settings text (JSON config) │ │
|
||||
│ │ createdAt timestamp │ │
|
||||
│ │ updatedAt timestamp │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 — TenantFeatures (JSONB — Sistema de Módulos)
|
||||
|
||||
O campo `features` na tabela `tenants` é um JSONB que controla **quais módulos da Arcádia estão habilitados** para cada tenant. O tipo é definido em `shared/schema.ts:821` e espelhado no frontend em `client/src/hooks/use-tenant-features.ts:3`.
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ TenantFeatures (JSONB) │
|
||||
│ │
|
||||
│ ┌─────────── Módulos Booleanos ──────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ ide → IDE integrada │ │
|
||||
│ │ whatsapp → WhatsApp multi-sessão │ │
|
||||
│ │ crm → CRM completo │ │
|
||||
│ │ erp → SOE/ERP │ │
|
||||
│ │ bi → Business Intelligence │ │
|
||||
│ │ manus → IA Manus (GPT-4o) │ │
|
||||
│ │ centralApis → APIs centrais │ │
|
||||
│ │ centralApisManage → Gerenciar APIs │ │
|
||||
│ │ comunidades → Chat interno │ │
|
||||
│ │ biblioteca → Biblioteca de conhecimento │ │
|
||||
│ │ bibliotecaPublish → Publicar na biblioteca │ │
|
||||
│ │ suporteN3 → Suporte nível 3 │ │
|
||||
│ │ retail → Módulo Varejo │ │
|
||||
│ │ plus → ERP Plus (Laravel) │ │
|
||||
│ │ fisco → Motor Fiscal │ │
|
||||
│ │ cockpit → Cockpit executivo │ │
|
||||
│ │ compass → Process Compass │ │
|
||||
│ │ production → Módulo Produção │ │
|
||||
│ │ support → Módulo Suporte │ │
|
||||
│ │ xosCrm → XOS CRM (Kanban) │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Configurações Numéricas ────────┐ │
|
||||
│ │ │ │
|
||||
│ │ whatsappSessions → Nº sessões WA (0-N) │ │
|
||||
│ │ maxChannels → Nº canais permitidos │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Configurações de Texto ─────────┐ │
|
||||
│ │ │ │
|
||||
│ │ ideMode → "none" | "no-code" | │ │
|
||||
│ │ "low-code" | "pro-code" │ │
|
||||
│ │ manusTools[] → Lista de tools habilitadas │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.3 — tenant_empresas (Matriz/Filiais)
|
||||
|
||||
Cada tenant pode ter **múltiplas empresas** (CNPJs) cadastradas — essencial para operações fiscais (NF-e/NFC-e) por filial.
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ tenant_empresas │
|
||||
│ Tabela: "tenant_empresas" — shared/schema.ts:889 │
|
||||
│ │
|
||||
│ ┌─────────── Identificação ──────────────────┐ │
|
||||
│ │ id serial PK │ │
|
||||
│ │ tenantId FK → tenants.id (CASCADE) │ │
|
||||
│ │ razaoSocial text NOT NULL │ │
|
||||
│ │ nomeFantasia text │ │
|
||||
│ │ cnpj text NOT NULL │ │
|
||||
│ │ ie text (inscrição estadual) │ │
|
||||
│ │ im text (inscrição municipal) │ │
|
||||
│ │ email, phone text │ │
|
||||
│ │ tipo "matriz" | "filial" │ │
|
||||
│ │ status "active" | "inactive" │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Endereço ───────────────────────┐ │
|
||||
│ │ cep, logradouro, numero, complemento, │ │
|
||||
│ │ bairro, cidade, uf, codigoIbge │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Fiscal ─────────────────────────┐ │
|
||||
│ │ regimeTributario "simples" | "presumido" │ │
|
||||
│ │ | "real" │ │
|
||||
│ │ certificadoDigitalId FK certificado │ │
|
||||
│ │ ambienteFiscal "producao" | "homologacao" │ │
|
||||
│ │ serieNfe integer (default 1) │ │
|
||||
│ │ serieNfce integer (default 1) │ │
|
||||
│ │ plusEmpresaId integer (link ERP Plus) │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Metadata ───────────────────────┐ │
|
||||
│ │ createdAt, updatedAt timestamp │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Uso típico:**
|
||||
|
||||
```
|
||||
Tenant "Empresa ABC" (id: 5)
|
||||
├── Empresa Matriz: "ABC Comércio LTDA" (CNPJ: 12.345.678/0001-00)
|
||||
│ regime: simples, ambiente: producao, série NF-e: 1
|
||||
├── Filial SP: "ABC Comércio SP" (CNPJ: 12.345.678/0002-81)
|
||||
│ regime: simples, ambiente: producao, série NF-e: 2
|
||||
└── Filial RJ: "ABC Comércio RJ" (CNPJ: 12.345.678/0003-62)
|
||||
regime: simples, ambiente: homologacao, série NF-e: 1
|
||||
```
|
||||
|
||||
### 2.4 — tenant_users (Membership)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ tenant_users │
|
||||
│ Tabela: "tenant_users" — shared/schema.ts:923 │
|
||||
│ │
|
||||
│ id serial PK │
|
||||
│ tenantId FK → tenants.id (CASCADE) │
|
||||
│ userId FK → users.id (CASCADE) │
|
||||
│ role "owner" | "admin" | "member" │
|
||||
│ isOwner "true" | "false" │
|
||||
│ createdAt timestamp │
|
||||
│ │
|
||||
│ → Link N:N entre users e tenants │
|
||||
│ → Um usuário pode pertencer a múltiplos tenants │
|
||||
│ → Cada tenant tem um owner + admins + members │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.5 — tenant_plans (Planos Disponíveis)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ tenant_plans │
|
||||
│ Tabela: "tenant_plans" — shared/schema.ts:933 │
|
||||
│ │
|
||||
│ id serial PK │
|
||||
│ code text UNIQUE ("free", "starter", "pro", │
|
||||
│ "enterprise", "partner_starter", "partner_pro") │
|
||||
│ name text NOT NULL │
|
||||
│ description text │
|
||||
│ tenantType "master" | "partner" | "client" │
|
||||
│ maxUsers integer (default 5) │
|
||||
│ maxStorageMb integer (default 1000) │
|
||||
│ features JSONB → TenantFeatures │
|
||||
│ monthlyPrice integer (centavos) │
|
||||
│ yearlyPrice integer (centavos) │
|
||||
│ trialDays integer (default 14) │
|
||||
│ isActive "true" | "false" │
|
||||
│ sortOrder integer │
|
||||
│ createdAt timestamp │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.6 — partner_clients (Relacionamento Parceiro↔Cliente)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ partner_clients │
|
||||
│ Tabela: "partner_clients" — shared/schema.ts:951 │
|
||||
│ │
|
||||
│ id serial PK │
|
||||
│ partnerId FK → tenants.id (CASCADE) │
|
||||
│ clientId FK → tenants.id (CASCADE) │
|
||||
│ commissionRate numeric(5,2) — Override do rate do parceiro │
|
||||
│ status "active" | "suspended" | "ended" │
|
||||
│ notes text │
|
||||
│ startedAt timestamp │
|
||||
│ endedAt timestamp │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.7 — partner_commissions (Comissões dos Parceiros)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ partner_commissions │
|
||||
│ Tabela: "partner_commissions" — shared/schema.ts:963 │
|
||||
│ │
|
||||
│ id serial PK │
|
||||
│ partnerId FK → tenants.id (CASCADE) │
|
||||
│ clientId FK → tenants.id (CASCADE) │
|
||||
│ referenceMonth text ("2026-01", "2026-02", ...) │
|
||||
│ clientPlanCode text (código do plano do cliente) │
|
||||
│ clientPlanValue integer (valor do plano em centavos) │
|
||||
│ commissionRate numeric(5,2) NOT NULL │
|
||||
│ commissionValue integer NOT NULL (valor em centavos) │
|
||||
│ status "pending" | "approved" | "paid" | "cancelled" │
|
||||
│ approvedAt timestamp │
|
||||
│ paidAt timestamp │
|
||||
│ paymentReference text │
|
||||
│ createdAt timestamp │
|
||||
│ │
|
||||
│ Fluxo: pending → approved → paid │
|
||||
│ │
|
||||
│ Exemplo: │
|
||||
│ Parceiro A tem Cliente X no plano "pro" (R$ 299/mês) │
|
||||
│ commissionRate: 20% │
|
||||
│ commissionValue: 5980 centavos (R$ 59,80) │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Planos Pré-definidos (Seed)
|
||||
|
||||
A rota `POST /api/admin/plans/seed` cria os 6 planos padrão:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ PLANOS PARA CLIENTES │
|
||||
├────────────┬──────────┬──────────┬──────────────────────────────────┤
|
||||
│ Plano │ Preço/mês│ Usuários │ Módulos │
|
||||
├────────────┼──────────┼──────────┼──────────────────────────────────┤
|
||||
│ Gratuito │ R$ 0 │ 2 │ erp, crm │
|
||||
│ (free) │ │ │ │
|
||||
├────────────┼──────────┼──────────┼──────────────────────────────────┤
|
||||
│ Starter │ R$ 99 │ 5 │ erp, crm, bi, fisco, │
|
||||
│ │ │ │ whatsapp, compass │
|
||||
├────────────┼──────────┼──────────┼──────────────────────────────────┤
|
||||
│ Pro │ R$ 299 │ 15 │ erp, crm, bi, fisco, retail, │
|
||||
│ │ │ │ plus, whatsapp, manus, cockpit, │
|
||||
│ │ │ │ compass, support, biblioteca │
|
||||
├────────────┼──────────┼──────────┼──────────────────────────────────┤
|
||||
│ Enterprise │ R$ 999 │ 100 │ TODOS os módulos │
|
||||
│ │ │ 50GB │ (erp, crm, bi, fisco, retail, │
|
||||
│ │ │ │ plus, whatsapp, manus, ide, │
|
||||
│ │ │ │ cockpit, compass, production, │
|
||||
│ │ │ │ support, xosCrm, centralApis, │
|
||||
│ │ │ │ comunidades, biblioteca) │
|
||||
└────────────┴──────────┴──────────┴──────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ PLANOS PARA PARCEIROS │
|
||||
├──────────────────┬──────────┬──────────┬────────────────────────────┤
|
||||
│ Plano │ Preço/mês│ Usuários │ Módulos │
|
||||
├──────────────────┼──────────┼──────────┼────────────────────────────┤
|
||||
│ Parceiro Starter │ R$ 199 │ 10 │ erp, crm, bi, fisco, │
|
||||
│ │ │ │ retail, whatsapp, compass, │
|
||||
│ │ │ │ support │
|
||||
├──────────────────┼──────────┼──────────┼────────────────────────────┤
|
||||
│ Parceiro Pro │ R$ 499 │ 50 │ TODOS os módulos │
|
||||
│ │ │ 25GB │ (igual Enterprise) │
|
||||
└──────────────────┴──────────┴──────────┴────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Sistema de Papéis (Roles)
|
||||
|
||||
A Arcádia opera com **dois níveis de papéis** que se complementam:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ SISTEMA DUPLO DE ROLES │
|
||||
│ │
|
||||
│ ┌─────────── Papel do Sistema (users.role) ──────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ "master" → Superadmin, acesso total, bypass de tenant │ │
|
||||
│ │ "admin" → Administrador, acessa /api/admin/* │ │
|
||||
│ │ "user" → Usuário padrão, sem acesso admin │ │
|
||||
│ │ │ │
|
||||
│ │ Verificado no middleware global de admin: │ │
|
||||
│ │ if (role !== "admin" && role !== "master") → 403 │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Papel no Tenant (tenant_users.role) ────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ "owner" → Dono do tenant, criador, full control │ │
|
||||
│ │ "admin" → Administrador do tenant │ │
|
||||
│ │ "member" → Membro comum, acesso operacional │ │
|
||||
│ │ │ │
|
||||
│ │ Usado para scoping de dados e permissões intra-tenant │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Combinação no login (getEnrichedUser): │
|
||||
│ user.role + user.tenantId + user.tenantType + user.tenantRole │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Controle de Acesso — Helpers
|
||||
|
||||
O backend implementa dois helpers essenciais em `server/admin/routes.ts`:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ getAllowedTenantIds(user) │
|
||||
│ │
|
||||
│ Retorna os IDs de tenants que o usuário pode acessar: │
|
||||
│ │
|
||||
│ • master → null (acesso total, sem filtro) │
|
||||
│ • partner → [seu tenantId, ...clientIds vinculados] │
|
||||
│ • client → [apenas seu tenantId] │
|
||||
│ │
|
||||
│ Usado em: GET /tenants, GET /partner-clients, GET /commissions, │
|
||||
│ GET /empresas, GET /tenants/hierarchy │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ canAccessTenant(user, tenantId) │
|
||||
│ │
|
||||
│ Verifica se o usuário pode operar sobre um tenant específico: │
|
||||
│ │
|
||||
│ • master → true (sempre) │
|
||||
│ • partner → true se tenantId ∈ getAllowedTenantIds() │
|
||||
│ • client → true apenas se tenantId === user.tenantId │
|
||||
│ │
|
||||
│ Usado em: PATCH /tenants/:id, DELETE /empresas/:id, │
|
||||
│ POST /empresas, etc. │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Middleware de Módulos (requireModule)
|
||||
|
||||
O middleware `requireModule` em `server/retail/routes.ts:25` bloqueia endpoints quando um módulo não está ativo para o tenant:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ requireModule(moduleKey: keyof TenantFeatures) │
|
||||
│ │
|
||||
│ Fluxo: │
|
||||
│ 1. Identifica tenantId do usuário logado │
|
||||
│ 2. Busca tenant no banco │
|
||||
│ 3. Verifica features[moduleKey] === true │
|
||||
│ 4. Se false → 403 com mensagem: │
|
||||
│ { │
|
||||
│ error: "Módulo 'X' não está ativo para este tenant", │
|
||||
│ moduleKey: "plus", │
|
||||
│ action: "Ative o módulo em Admin → Módulos" │
|
||||
│ } │
|
||||
│ │
|
||||
│ Exemplo de uso: │
|
||||
│ router.post("/plus/sync/customers", │
|
||||
│ requireModule("plus"), ← Bloqueia se Plus não está ativo │
|
||||
│ async (req, res) => { ... } │
|
||||
│ ); │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Hook Frontend (useTenantFeatures)
|
||||
|
||||
O hook `useTenantFeatures()` em `client/src/hooks/use-tenant-features.ts` fornece acesso às features do tenant no frontend:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ useTenantFeatures() │
|
||||
│ │
|
||||
│ Fonte: GET /api/soe/tenant/modules │
|
||||
│ Cache: 60 segundos (staleTime: 60000) │
|
||||
│ │
|
||||
│ Retorna: │
|
||||
│ { │
|
||||
│ features: TenantFeatures, ← Objeto completo de features │
|
||||
│ isLoading: boolean, │
|
||||
│ plan: string, ← Código do plano atual │
|
||||
│ isEnabled: (key) => bool, ← Verificação rápida │
|
||||
│ } │
|
||||
│ │
|
||||
│ Defaults (quando não logado / sem tenant): │
|
||||
│ ide: true, crm: true, erp: true, manus: true, compass: true │
|
||||
│ Todos os demais: false │
|
||||
│ │
|
||||
│ Uso no componente: │
|
||||
│ const { isEnabled } = useTenantFeatures(); │
|
||||
│ if (isEnabled("bi")) { /* mostra menu BI */ } │
|
||||
│ if (isEnabled("retail")) { /* mostra módulo varejo */ } │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Interface de Administração
|
||||
|
||||
### 8.1 — Admin.tsx (Página Principal)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Admin.tsx — client/src/pages/Admin.tsx (2.635 linhas) │
|
||||
│ Rota: /admin │
|
||||
│ │
|
||||
│ 6 Abas: │
|
||||
│ ┌─────────────┬──────────┬────────────┬───────────┬──────────────┐ │
|
||||
│ │ Conexões │ Tarefas │ Knowledge │ Bibliotecas│ Módulos │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ WhatsApp │ Dev │ Knowledge │ Conteúdo │ Ativação │ │
|
||||
│ │ ERPNext │ Center │ Graph │ Docs │ por tenant │ │
|
||||
│ │ APIs │ Pipeline │ Q&A │ Templates │ Toggle on/ │ │
|
||||
│ │ │ │ │ │ off módulos │ │
|
||||
│ └─────────────┴──────────┴────────────┴───────────┴──────────────┘ │
|
||||
│ ┌─────────────┐ │
|
||||
│ │Casa de │ │
|
||||
│ │Máquinas │ │
|
||||
│ │ │ │
|
||||
│ │ Status dos │ │
|
||||
│ │ motores │ │
|
||||
│ │ (:8002-8006)│ │
|
||||
│ └─────────────┘ │
|
||||
│ │
|
||||
│ Aba "Módulos" renderiza: <MultiTenantSection /> │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 8.2 — MultiTenantSection (Gestão Completa)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ MultiTenantSection.tsx (1.486 linhas) │
|
||||
│ client/src/components/MultiTenantSection.tsx │
|
||||
│ │
|
||||
│ 4 Abas internas: │
|
||||
│ │
|
||||
│ ┌─ Tenants ──────────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ Lista de tenants com: │ │
|
||||
│ │ • Nome, tipo (Master/Parceiro/Cliente) │ │
|
||||
│ │ • Plano atual e status │ │
|
||||
│ │ • Nº de filhos (childCount) │ │
|
||||
│ │ • Tenant pai (parentTenant) │ │
|
||||
│ │ • Ações: Editar, Ativar/Desativar módulos │ │
|
||||
│ │ │ │
|
||||
│ │ Formulário de criação: │ │
|
||||
│ │ • Nome, email, phone │ │
|
||||
│ │ • Tipo (master/partner/client) │ │
|
||||
│ │ • Plano (seleciona do tenant_plans) │ │
|
||||
│ │ • Tenant pai (para hierarquia) │ │
|
||||
│ │ • Features auto-preenchidas pelo plano selecionado │ │
|
||||
│ │ • Toggle individual de cada módulo │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─ Planos ───────────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ CRUD de planos com: │ │
|
||||
│ │ • Código, nome, descrição │ │
|
||||
│ │ • Tipo de tenant (client/partner) │ │
|
||||
│ │ • Preço mensal e anual (centavos) │ │
|
||||
│ │ • Max usuários e storage │ │
|
||||
│ │ • Features (toggle de cada módulo) │ │
|
||||
│ │ • Botão "Seed" para criar planos padrão │ │
|
||||
│ │ • Botão "Propagar" para aplicar features do plano │ │
|
||||
│ │ a todos os tenants que usam esse plano │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─ Comissões ────────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ Lista de comissões de parceiros: │ │
|
||||
│ │ • Parceiro, Cliente, Mês referência │ │
|
||||
│ │ • Valor do plano, taxa de comissão, valor da comissão │ │
|
||||
│ │ • Status (pending → approved → paid) │ │
|
||||
│ │ • Ações: Aprovar, Marcar como paga │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─ Empresas ─────────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ EmpresasSubSection: │ │
|
||||
│ │ • Lista de empresas (Matriz/Filiais) por tenant │ │
|
||||
│ │ • CNPJ, Razão Social, Nome Fantasia │ │
|
||||
│ │ • IE, IM, Regime Tributário │ │
|
||||
│ │ • Endereço completo (CEP, logradouro, cidade, UF) │ │
|
||||
│ │ • Config Fiscal (ambiente, série NF-e, série NFC-e) │ │
|
||||
│ │ • Link com ERP Plus (plusEmpresaId) │ │
|
||||
│ │ • Ações: Criar, Editar, Excluir │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. API REST Completa (/api/admin/*)
|
||||
|
||||
### Tenants
|
||||
|
||||
| Método | Endpoint | Função | Acesso |
|
||||
|--------|----------|--------|--------|
|
||||
| GET | `/tenants` | Lista tenants (filtrado por acesso) | master/partner/client |
|
||||
| POST | `/tenants` | Cria tenant (resolve features do plano) | master/admin |
|
||||
| PATCH | `/tenants/:id` | Atualiza tenant (canAccessTenant) | master/partner |
|
||||
| DELETE | `/tenants/:id` | Exclui tenant (proteção master) | somente master |
|
||||
| GET | `/tenants/hierarchy` | Árvore hierárquica de tenants | master/partner |
|
||||
| GET | `/tenants/stats` | Estatísticas (total, por tipo, por status) | master/admin |
|
||||
|
||||
### Planos
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/plans` | Lista todos os planos |
|
||||
| POST | `/plans` | Cria novo plano |
|
||||
| PATCH | `/plans/:id` | Atualiza plano |
|
||||
| DELETE | `/plans/:id` | Exclui plano |
|
||||
| POST | `/plans/:id/propagate` | Propaga features do plano para todos os tenants que o usam |
|
||||
| POST | `/plans/seed` | Cria 6 planos padrão |
|
||||
|
||||
### Parceiro-Cliente
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/partner-clients` | Lista relacionamentos parceiro↔cliente |
|
||||
| POST | `/partner-clients` | Cria vínculo parceiro↔cliente |
|
||||
|
||||
### Comissões de Parceiros
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/commissions` | Lista comissões (filtro por partnerId, status) |
|
||||
| PATCH | `/commissions/:id/approve` | Aprova comissão |
|
||||
| PATCH | `/commissions/:id/pay` | Marca como paga |
|
||||
|
||||
### Empresas (Matriz/Filiais)
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/empresas` | Lista empresas (filtro por tenantId + acesso) |
|
||||
| GET | `/empresas/:id` | Detalhe da empresa |
|
||||
| POST | `/empresas` | Cria empresa (verifica canAccessTenant) |
|
||||
| PATCH | `/empresas/:id` | Atualiza empresa |
|
||||
| DELETE | `/empresas/:id` | Exclui empresa |
|
||||
|
||||
### Outros Admin
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/profiles` | Lista perfis de usuários |
|
||||
| POST | `/profiles` | Cria perfil |
|
||||
| PATCH | `/profiles/:id` | Atualiza perfil |
|
||||
| DELETE | `/profiles/:id` | Exclui perfil |
|
||||
| GET | `/users` | Lista usuários (com tenant info) |
|
||||
| PATCH | `/users/:id` | Atualiza usuário |
|
||||
| PATCH | `/users/:id/status` | Altera status do usuário |
|
||||
| PATCH | `/partners/:id/status` | Altera status de parceiro |
|
||||
| PATCH | `/clients/:id/status` | Altera status de cliente |
|
||||
| GET | `/stats` | Estatísticas gerais (tenants, users, leads) |
|
||||
| GET | `/libraries` | Lista bibliotecas de conteúdo |
|
||||
|
||||
**Total: 33 endpoints de administração**
|
||||
|
||||
---
|
||||
|
||||
## 10. Fluxo Completo — Onboarding de Tenant
|
||||
|
||||
```
|
||||
┌──────────┐ ┌────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ 1. CRIAR │───▶│ 2. PLANO │───▶│ 3. FEATURES │───▶│ 4. EMPRESAS │
|
||||
│ TENANT │ │ │ │ │ │ │
|
||||
│ │ │ Seleciona │ │ Auto-resolve │ │ Cadastra │
|
||||
│ POST │ │ plano │ │ do plano ou │ │ Matriz │
|
||||
│ /admin/ │ │ (free, │ │ toggle │ │ + Filiais │
|
||||
│ tenants │ │ starter, │ │ individual │ │ │
|
||||
│ │ │ pro, etc) │ │ de módulos │ │ CNPJ, IE, │
|
||||
│ name, │ │ │ │ │ │ endereço, │
|
||||
│ email, │ │ maxUsers │ │ ide: true │ │ regime │
|
||||
│ type │ │ maxStorage │ │ crm: true │ │ tributário, │
|
||||
│ │ │ preço │ │ bi: false │ │ certificado │
|
||||
│ │ │ │ │ ... │ │ digital │
|
||||
└──────────┘ └────────────┘ └──────────────┘ └──────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐
|
||||
│ 5. VINCULAR │ │ 6. COMISSÕES │ │ 7. USUÁRIOS │
|
||||
│ PARCEIRO │ │ │ │ │
|
||||
│ (se partner) │ │ Automáticas │ │ Convida users │
|
||||
│ │ │ mensais │ │ tenant_users │
|
||||
│ POST /admin/ │ │ baseadas no │ │ roles: owner/admin/ │
|
||||
│ partner- │ │ plano do │ │ member │
|
||||
│ clients │ │ cliente │ │ │
|
||||
│ │ │ │ │ Scoping automático │
|
||||
│ partnerId │ │ pending → │ │ de todos os dados │
|
||||
│ clientId │ │ approved → │ │ por tenantId │
|
||||
│ commission% │ │ paid │ │ │
|
||||
└──────────────┘ └──────────────┘ └──────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Propagação de Plano
|
||||
|
||||
Quando um plano é atualizado, a feature de **propagação** atualiza todos os tenants que usam esse plano:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ POST /api/admin/plans/:id/propagate │
|
||||
│ │
|
||||
│ 1. Busca o plano por ID │
|
||||
│ 2. Pega features, maxUsers, maxStorageMb do plano │
|
||||
│ 3. Atualiza TODOS os tenants onde plan === plan.code: │
|
||||
│ UPDATE tenants SET │
|
||||
│ features = plan.features, │
|
||||
│ maxUsers = plan.maxUsers, │
|
||||
│ maxStorageMb = plan.maxStorageMb │
|
||||
│ WHERE plan = 'pro' │
|
||||
│ 4. Retorna: { propagated: 15, planCode: "pro" } │
|
||||
│ │
|
||||
│ Exemplo: │
|
||||
│ Plano "pro" ganha módulo "production" → │
|
||||
│ Propagar → 15 tenants "pro" recebem production: true │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Isolamento de Dados
|
||||
|
||||
Toda query de dados respeita o escopo do tenant:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ISOLAMENTO POR tenantId │
|
||||
│ │
|
||||
│ Padrão em todas as queries: │
|
||||
│ │
|
||||
│ const tenantId = (req.user as any).tenantId; │
|
||||
│ const data = await db.select() │
|
||||
│ .from(tabela) │
|
||||
│ .where(eq(tabela.tenantId, tenantId)); │
|
||||
│ │
|
||||
│ Tabelas com tenantId: │
|
||||
│ • tenants (hierarquia) • crm_channels │
|
||||
│ • tenant_empresas • crm_threads │
|
||||
│ • crm_leads • crm_events │
|
||||
│ • crm_clients • crm_campaigns │
|
||||
│ • crm_opportunities • crm_products │
|
||||
│ • crm_partners • crm_contracts │
|
||||
│ • crm_pipeline_stages • crm_commission_rules │
|
||||
│ • crm_proposals • crm_quick_messages │
|
||||
│ • retail_activity_feed • valuation_* │
|
||||
│ │
|
||||
│ Resultado: Dados do Tenant A NUNCA são vistos pelo Tenant B │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. Arquivos-Chave
|
||||
|
||||
| Arquivo | Função | Linhas |
|
||||
|---------|--------|--------|
|
||||
| `shared/schema.ts` (L821-977) | TenantFeatures type + 6 tabelas tenant | ~160 |
|
||||
| `server/admin/routes.ts` | API admin (33 endpoints) | 876 |
|
||||
| `client/src/pages/Admin.tsx` | Página admin (6 abas) | 2.635 |
|
||||
| `client/src/components/MultiTenantSection.tsx` | Gestão multi-tenant (4 sub-abas) | 1.486 |
|
||||
| `client/src/hooks/use-tenant-features.ts` | Hook de features no frontend | 59 |
|
||||
| `server/retail/routes.ts` (L25) | Middleware requireModule | ~25 |
|
||||
| `server/storage.ts` (getEnrichedUser) | Enriquece user com tenant info | — |
|
||||
|
||||
---
|
||||
|
||||
## 14. Resumo Visual
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ARCÁDIA MULTI-TENANT STACK │
|
||||
│ │
|
||||
│ ┌───── Autenticação ──────────────────────────────────────────┐ │
|
||||
│ │ Login → getEnrichedUser → user + tenantId + tenantType │ │
|
||||
│ │ + tenantRole + features │ │
|
||||
│ └─────────────────────────────────────────┬───────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────────────────────────▼───────────────────┐ │
|
||||
│ │ Middleware │ │
|
||||
│ │ ┌──────────┐ ┌─────────────┐ ┌────────────────────┐ │ │
|
||||
│ │ │requireAuth│ │requireModule │ │getAllowedTenantIds │ │ │
|
||||
│ │ │role check │ │feature check │ │canAccessTenant │ │ │
|
||||
│ │ └──────────┘ └─────────────┘ └────────────────────┘ │ │
|
||||
│ └─────────────────────────────────────────┬───────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────────────────────────▼───────────────────┐ │
|
||||
│ │ API /api/admin/* │ │
|
||||
│ │ 33 endpoints: Tenants, Plans, Empresas, Comissões │ │
|
||||
│ └─────────────────────────────────────────┬───────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────────────────────────▼───────────────────┐ │
|
||||
│ │ PostgreSQL │ │
|
||||
│ │ 6 tabelas: tenants, tenant_users, tenant_plans, │ │
|
||||
│ │ tenant_empresas, partner_clients, partner_commissions │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───── Frontend ──────────────────────────────────────────────┐ │
|
||||
│ │ Admin.tsx (6 abas) → MultiTenantSection (4 sub-abas) │ │
|
||||
│ │ useTenantFeatures() hook → GET /api/soe/tenant/modules │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
|
@ -0,0 +1,812 @@
|
|||
# Arcádia Tenants — Mapa Completo da Gestão Multi-Tenant
|
||||
|
||||
> Mapa da arquitetura multi-tenant da Arcádia Suite: hierarquia
|
||||
> Master/Parceiro/Cliente, sistema de planos, ativação de módulos,
|
||||
> empresas (Matriz/Filiais), comissões de parceiros, controle de acesso,
|
||||
> e middleware de isolamento.
|
||||
> Atualizado em: Março 2026
|
||||
|
||||
---
|
||||
|
||||
## 1. Visão Geral
|
||||
|
||||
A Arcádia Suite opera em modelo **multi-tenant hierárquico** com três níveis. Cada tenant possui um plano que define quais módulos da plataforma estão habilitados, e pode conter múltiplas empresas (CNPJ) para operações fiscais.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ HIERARQUIA DE TENANTS │
|
||||
│ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ MASTER │ ← Operador da plataforma │
|
||||
│ │ │ ← Vê tudo, controla tudo │
|
||||
│ │ tenantType: │ ← Pode criar parceiros │
|
||||
│ │ "master" │ ← Pode excluir tenants │
|
||||
│ └──────┬───────┘ │
|
||||
│ │ │
|
||||
│ ┌────────────┼────────────┐ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ PARCEIRO A │ │ PARCEIRO B │ │ PARCEIRO C │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ tenantType: │ │ Consultorias │ │ Integradores │ │
|
||||
│ │ "partner" │ │ Revendas │ │ Franquias │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ partnerCode: │ │ comissão: % │ │ plano: │ │
|
||||
│ │ "PARC-001" │ │ sobre clientes│ │partner_pro │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
||||
│ │ │ │ │
|
||||
│ ┌────┴────┐ ┌───┴───┐ ┌───┴───┐ │
|
||||
│ ▼ ▼ ▼ ▼ ▼ ▼ │
|
||||
│ ┌────────┐┌────────┐┌────────┐┌────────┐┌────────┐┌────────┐ │
|
||||
│ │Cliente1││Cliente2││Cliente3││Cliente4││Cliente5││Cliente6│ │
|
||||
│ │ ││ ││ ││ ││ ││ │ │
|
||||
│ │tenant ││tenant ││tenant ││tenant ││tenant ││tenant │ │
|
||||
│ │Type: ││Type: ││Type: ││Type: ││Type: ││Type: │ │
|
||||
│ │"client"││"client"││"client"││"client"││"client"││"client"│ │
|
||||
│ │ ││ ││ ││ ││ ││ │ │
|
||||
│ │parent: ││parent: ││parent: ││parent: ││parent: ││parent: │ │
|
||||
│ │Parc.A ││Parc.A ││Parc.B ││Parc.B ││Parc.C ││Parc.C │ │
|
||||
│ │ ││ ││ ││ ││ ││ │ │
|
||||
│ │Plano: ││Plano: ││Plano: ││Plano: ││Plano: ││Plano: │ │
|
||||
│ │starter ││pro ││free ││enterpr.││starter ││pro │ │
|
||||
│ └────────┘└────────┘└────────┘└────────┘└────────┘└────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Visibilidade por tipo:
|
||||
|
||||
| Tipo | Vê | Cria | Exclui |
|
||||
|------|-----|------|--------|
|
||||
| **Master** | Todos os tenants | Parceiros e Clientes | Qualquer (exceto master) |
|
||||
| **Partner** | Seu tenant + seus clientes | Apenas clientes vinculados | Nenhum |
|
||||
| **Client** | Apenas seu próprio tenant | Nenhum | Nenhum |
|
||||
|
||||
---
|
||||
|
||||
## 2. Banco de Dados — Tabelas de Tenant
|
||||
|
||||
### 2.1 — tenants (Tabela Principal)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ tenants │
|
||||
│ Tabela: "tenants" — shared/schema.ts:849 │
|
||||
│ │
|
||||
│ ┌─────────── Identificação ──────────────────┐ │
|
||||
│ │ id serial PK │ │
|
||||
│ │ name text NOT NULL │ │
|
||||
│ │ slug text UNIQUE │ │
|
||||
│ │ email text │ │
|
||||
│ │ phone text │ │
|
||||
│ │ logoUrl text │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Hierarquia ─────────────────────┐ │
|
||||
│ │ tenantType "master" | "partner" | │ │
|
||||
│ │ "client" │ │
|
||||
│ │ parentTenantId integer (self-ref FK) │ │
|
||||
│ │ partnerCode text (código único parceiro) │ │
|
||||
│ │ commissionRate numeric(5,2) (% comissão) │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Plano e Limites ────────────────┐ │
|
||||
│ │ plan "free" | "starter" | "pro" │ │
|
||||
│ │ | "enterprise" | │ │
|
||||
│ │ "partner_starter" | │ │
|
||||
│ │ "partner_pro" │ │
|
||||
│ │ status "active" | "trial" | │ │
|
||||
│ │ "suspended" | "cancelled" │ │
|
||||
│ │ maxUsers integer (default 5) │ │
|
||||
│ │ maxStorageMb integer (default 1000) │ │
|
||||
│ │ features JSONB → TenantFeatures │ ← Módulos │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Billing ────────────────────────┐ │
|
||||
│ │ billingEmail text │ │
|
||||
│ │ trialEndsAt timestamp │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Dados Empresariais ─────────────┐ │
|
||||
│ │ cnpj text │ │
|
||||
│ │ tradeName text │ │
|
||||
│ │ address text │ │
|
||||
│ │ city, state text │ │
|
||||
│ │ segment text │ │
|
||||
│ │ notes, source text │ │
|
||||
│ │ commercialContact / commercialPhone │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Metadata ───────────────────────┐ │
|
||||
│ │ settings text (JSON config) │ │
|
||||
│ │ createdAt timestamp │ │
|
||||
│ │ updatedAt timestamp │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 — TenantFeatures (JSONB — Sistema de Módulos)
|
||||
|
||||
O campo `features` na tabela `tenants` é um JSONB que controla **quais módulos da Arcádia estão habilitados** para cada tenant. O tipo é definido em `shared/schema.ts:821` e espelhado no frontend em `client/src/hooks/use-tenant-features.ts:3`.
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ TenantFeatures (JSONB) │
|
||||
│ │
|
||||
│ ┌─────────── Módulos Booleanos ──────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ ide → IDE integrada │ │
|
||||
│ │ whatsapp → WhatsApp multi-sessão │ │
|
||||
│ │ crm → CRM completo │ │
|
||||
│ │ erp → SOE/ERP │ │
|
||||
│ │ bi → Business Intelligence │ │
|
||||
│ │ manus → IA Manus (GPT-4o) │ │
|
||||
│ │ centralApis → APIs centrais │ │
|
||||
│ │ centralApisManage → Gerenciar APIs │ │
|
||||
│ │ comunidades → Chat interno │ │
|
||||
│ │ biblioteca → Biblioteca de conhecimento │ │
|
||||
│ │ bibliotecaPublish → Publicar na biblioteca │ │
|
||||
│ │ suporteN3 → Suporte nível 3 │ │
|
||||
│ │ retail → Módulo Varejo │ │
|
||||
│ │ plus → ERP Plus (Laravel) │ │
|
||||
│ │ fisco → Motor Fiscal │ │
|
||||
│ │ cockpit → Cockpit executivo │ │
|
||||
│ │ compass → Process Compass │ │
|
||||
│ │ production → Módulo Produção │ │
|
||||
│ │ support → Módulo Suporte │ │
|
||||
│ │ xosCrm → XOS CRM (Kanban) │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Configurações Numéricas ────────┐ │
|
||||
│ │ │ │
|
||||
│ │ whatsappSessions → Nº sessões WA (0-N) │ │
|
||||
│ │ maxChannels → Nº canais permitidos │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Configurações de Texto ─────────┐ │
|
||||
│ │ │ │
|
||||
│ │ ideMode → "none" | "no-code" | │ │
|
||||
│ │ "low-code" | "pro-code" │ │
|
||||
│ │ manusTools[] → Lista de tools habilitadas │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.3 — tenant_empresas (Matriz/Filiais)
|
||||
|
||||
Cada tenant pode ter **múltiplas empresas** (CNPJs) cadastradas — essencial para operações fiscais (NF-e/NFC-e) por filial.
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ tenant_empresas │
|
||||
│ Tabela: "tenant_empresas" — shared/schema.ts:889 │
|
||||
│ │
|
||||
│ ┌─────────── Identificação ──────────────────┐ │
|
||||
│ │ id serial PK │ │
|
||||
│ │ tenantId FK → tenants.id (CASCADE) │ │
|
||||
│ │ razaoSocial text NOT NULL │ │
|
||||
│ │ nomeFantasia text │ │
|
||||
│ │ cnpj text NOT NULL │ │
|
||||
│ │ ie text (inscrição estadual) │ │
|
||||
│ │ im text (inscrição municipal) │ │
|
||||
│ │ email, phone text │ │
|
||||
│ │ tipo "matriz" | "filial" │ │
|
||||
│ │ status "active" | "inactive" │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Endereço ───────────────────────┐ │
|
||||
│ │ cep, logradouro, numero, complemento, │ │
|
||||
│ │ bairro, cidade, uf, codigoIbge │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Fiscal ─────────────────────────┐ │
|
||||
│ │ regimeTributario "simples" | "presumido" │ │
|
||||
│ │ | "real" │ │
|
||||
│ │ certificadoDigitalId FK certificado │ │
|
||||
│ │ ambienteFiscal "producao" | "homologacao" │ │
|
||||
│ │ serieNfe integer (default 1) │ │
|
||||
│ │ serieNfce integer (default 1) │ │
|
||||
│ │ plusEmpresaId integer (link ERP Plus) │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Metadata ───────────────────────┐ │
|
||||
│ │ createdAt, updatedAt timestamp │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Uso típico:**
|
||||
|
||||
```
|
||||
Tenant "Empresa ABC" (id: 5)
|
||||
├── Empresa Matriz: "ABC Comércio LTDA" (CNPJ: 12.345.678/0001-00)
|
||||
│ regime: simples, ambiente: producao, série NF-e: 1
|
||||
├── Filial SP: "ABC Comércio SP" (CNPJ: 12.345.678/0002-81)
|
||||
│ regime: simples, ambiente: producao, série NF-e: 2
|
||||
└── Filial RJ: "ABC Comércio RJ" (CNPJ: 12.345.678/0003-62)
|
||||
regime: simples, ambiente: homologacao, série NF-e: 1
|
||||
```
|
||||
|
||||
### 2.4 — tenant_users (Membership)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ tenant_users │
|
||||
│ Tabela: "tenant_users" — shared/schema.ts:923 │
|
||||
│ │
|
||||
│ id serial PK │
|
||||
│ tenantId FK → tenants.id (CASCADE) │
|
||||
│ userId FK → users.id (CASCADE) │
|
||||
│ role "owner" | "admin" | "member" │
|
||||
│ isOwner "true" | "false" │
|
||||
│ createdAt timestamp │
|
||||
│ │
|
||||
│ → Link N:N entre users e tenants │
|
||||
│ → Um usuário pode pertencer a múltiplos tenants │
|
||||
│ → Cada tenant tem um owner + admins + members │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.5 — tenant_plans (Planos Disponíveis)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ tenant_plans │
|
||||
│ Tabela: "tenant_plans" — shared/schema.ts:933 │
|
||||
│ │
|
||||
│ id serial PK │
|
||||
│ code text UNIQUE ("free", "starter", "pro", │
|
||||
│ "enterprise", "partner_starter", "partner_pro") │
|
||||
│ name text NOT NULL │
|
||||
│ description text │
|
||||
│ tenantType "master" | "partner" | "client" │
|
||||
│ maxUsers integer (default 5) │
|
||||
│ maxStorageMb integer (default 1000) │
|
||||
│ features JSONB → TenantFeatures │
|
||||
│ monthlyPrice integer (centavos) │
|
||||
│ yearlyPrice integer (centavos) │
|
||||
│ trialDays integer (default 14) │
|
||||
│ isActive "true" | "false" │
|
||||
│ sortOrder integer │
|
||||
│ createdAt timestamp │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.6 — partner_clients (Relacionamento Parceiro↔Cliente)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ partner_clients │
|
||||
│ Tabela: "partner_clients" — shared/schema.ts:951 │
|
||||
│ │
|
||||
│ id serial PK │
|
||||
│ partnerId FK → tenants.id (CASCADE) │
|
||||
│ clientId FK → tenants.id (CASCADE) │
|
||||
│ commissionRate numeric(5,2) — Override do rate do parceiro │
|
||||
│ status "active" | "suspended" | "ended" │
|
||||
│ notes text │
|
||||
│ startedAt timestamp │
|
||||
│ endedAt timestamp │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.7 — partner_commissions (Comissões dos Parceiros)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ partner_commissions │
|
||||
│ Tabela: "partner_commissions" — shared/schema.ts:963 │
|
||||
│ │
|
||||
│ id serial PK │
|
||||
│ partnerId FK → tenants.id (CASCADE) │
|
||||
│ clientId FK → tenants.id (CASCADE) │
|
||||
│ referenceMonth text ("2026-01", "2026-02", ...) │
|
||||
│ clientPlanCode text (código do plano do cliente) │
|
||||
│ clientPlanValue integer (valor do plano em centavos) │
|
||||
│ commissionRate numeric(5,2) NOT NULL │
|
||||
│ commissionValue integer NOT NULL (valor em centavos) │
|
||||
│ status "pending" | "approved" | "paid" | "cancelled" │
|
||||
│ approvedAt timestamp │
|
||||
│ paidAt timestamp │
|
||||
│ paymentReference text │
|
||||
│ createdAt timestamp │
|
||||
│ │
|
||||
│ Fluxo: pending → approved → paid │
|
||||
│ │
|
||||
│ Exemplo: │
|
||||
│ Parceiro A tem Cliente X no plano "pro" (R$ 299/mês) │
|
||||
│ commissionRate: 20% │
|
||||
│ commissionValue: 5980 centavos (R$ 59,80) │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Planos Pré-definidos (Seed)
|
||||
|
||||
A rota `POST /api/admin/plans/seed` cria os 6 planos padrão:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ PLANOS PARA CLIENTES │
|
||||
├────────────┬──────────┬──────────┬──────────────────────────────────┤
|
||||
│ Plano │ Preço/mês│ Usuários │ Módulos │
|
||||
├────────────┼──────────┼──────────┼──────────────────────────────────┤
|
||||
│ Gratuito │ R$ 0 │ 2 │ erp, crm │
|
||||
│ (free) │ │ │ │
|
||||
├────────────┼──────────┼──────────┼──────────────────────────────────┤
|
||||
│ Starter │ R$ 99 │ 5 │ erp, crm, bi, fisco, │
|
||||
│ │ │ │ whatsapp, compass │
|
||||
├────────────┼──────────┼──────────┼──────────────────────────────────┤
|
||||
│ Pro │ R$ 299 │ 15 │ erp, crm, bi, fisco, retail, │
|
||||
│ │ │ │ plus, whatsapp, manus, cockpit, │
|
||||
│ │ │ │ compass, support, biblioteca │
|
||||
├────────────┼──────────┼──────────┼──────────────────────────────────┤
|
||||
│ Enterprise │ R$ 999 │ 100 │ TODOS os módulos │
|
||||
│ │ │ 50GB │ (erp, crm, bi, fisco, retail, │
|
||||
│ │ │ │ plus, whatsapp, manus, ide, │
|
||||
│ │ │ │ cockpit, compass, production, │
|
||||
│ │ │ │ support, xosCrm, centralApis, │
|
||||
│ │ │ │ comunidades, biblioteca) │
|
||||
└────────────┴──────────┴──────────┴──────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ PLANOS PARA PARCEIROS │
|
||||
├──────────────────┬──────────┬──────────┬────────────────────────────┤
|
||||
│ Plano │ Preço/mês│ Usuários │ Módulos │
|
||||
├──────────────────┼──────────┼──────────┼────────────────────────────┤
|
||||
│ Parceiro Starter │ R$ 199 │ 10 │ erp, crm, bi, fisco, │
|
||||
│ │ │ │ retail, whatsapp, compass, │
|
||||
│ │ │ │ support │
|
||||
├──────────────────┼──────────┼──────────┼────────────────────────────┤
|
||||
│ Parceiro Pro │ R$ 499 │ 50 │ TODOS os módulos │
|
||||
│ │ │ 25GB │ (igual Enterprise) │
|
||||
└──────────────────┴──────────┴──────────┴────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Sistema de Papéis (Roles)
|
||||
|
||||
A Arcádia opera com **dois níveis de papéis** que se complementam:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ SISTEMA DUPLO DE ROLES │
|
||||
│ │
|
||||
│ ┌─────────── Papel do Sistema (users.role) ──────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ "master" → Superadmin, acesso total, bypass de tenant │ │
|
||||
│ │ "admin" → Administrador, acessa /api/admin/* │ │
|
||||
│ │ "user" → Usuário padrão, sem acesso admin │ │
|
||||
│ │ │ │
|
||||
│ │ Verificado no middleware global de admin: │ │
|
||||
│ │ if (role !== "admin" && role !== "master") → 403 │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────── Papel no Tenant (tenant_users.role) ────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ "owner" → Dono do tenant, criador, full control │ │
|
||||
│ │ "admin" → Administrador do tenant │ │
|
||||
│ │ "member" → Membro comum, acesso operacional │ │
|
||||
│ │ │ │
|
||||
│ │ Usado para scoping de dados e permissões intra-tenant │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Combinação no login (getEnrichedUser): │
|
||||
│ user.role + user.tenantId + user.tenantType + user.tenantRole │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Controle de Acesso — Helpers
|
||||
|
||||
O backend implementa dois helpers essenciais em `server/admin/routes.ts`:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ getAllowedTenantIds(user) │
|
||||
│ │
|
||||
│ Retorna os IDs de tenants que o usuário pode acessar: │
|
||||
│ │
|
||||
│ • master → null (acesso total, sem filtro) │
|
||||
│ • partner → [seu tenantId, ...clientIds vinculados] │
|
||||
│ • client → [apenas seu tenantId] │
|
||||
│ │
|
||||
│ Usado em: GET /tenants, GET /partner-clients, GET /commissions, │
|
||||
│ GET /empresas, GET /tenants/hierarchy │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ canAccessTenant(user, tenantId) │
|
||||
│ │
|
||||
│ Verifica se o usuário pode operar sobre um tenant específico: │
|
||||
│ │
|
||||
│ • master → true (sempre) │
|
||||
│ • partner → true se tenantId ∈ getAllowedTenantIds() │
|
||||
│ • client → true apenas se tenantId === user.tenantId │
|
||||
│ │
|
||||
│ Usado em: PATCH /tenants/:id, DELETE /empresas/:id, │
|
||||
│ POST /empresas, etc. │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Middleware de Módulos (requireModule)
|
||||
|
||||
O middleware `requireModule` em `server/retail/routes.ts:25` bloqueia endpoints quando um módulo não está ativo para o tenant:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ requireModule(moduleKey: keyof TenantFeatures) │
|
||||
│ │
|
||||
│ Fluxo: │
|
||||
│ 1. Identifica tenantId do usuário logado │
|
||||
│ 2. Busca tenant no banco │
|
||||
│ 3. Verifica features[moduleKey] === true │
|
||||
│ 4. Se false → 403 com mensagem: │
|
||||
│ { │
|
||||
│ error: "Módulo 'X' não está ativo para este tenant", │
|
||||
│ moduleKey: "plus", │
|
||||
│ action: "Ative o módulo em Admin → Módulos" │
|
||||
│ } │
|
||||
│ │
|
||||
│ Exemplo de uso: │
|
||||
│ router.post("/plus/sync/customers", │
|
||||
│ requireModule("plus"), ← Bloqueia se Plus não está ativo │
|
||||
│ async (req, res) => { ... } │
|
||||
│ ); │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Hook Frontend (useTenantFeatures)
|
||||
|
||||
O hook `useTenantFeatures()` em `client/src/hooks/use-tenant-features.ts` fornece acesso às features do tenant no frontend:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ useTenantFeatures() │
|
||||
│ │
|
||||
│ Fonte: GET /api/soe/tenant/modules │
|
||||
│ Cache: 60 segundos (staleTime: 60000) │
|
||||
│ │
|
||||
│ Retorna: │
|
||||
│ { │
|
||||
│ features: TenantFeatures, ← Objeto completo de features │
|
||||
│ isLoading: boolean, │
|
||||
│ plan: string, ← Código do plano atual │
|
||||
│ isEnabled: (key) => bool, ← Verificação rápida │
|
||||
│ } │
|
||||
│ │
|
||||
│ Defaults (quando não logado / sem tenant): │
|
||||
│ ide: true, crm: true, erp: true, manus: true, compass: true │
|
||||
│ Todos os demais: false │
|
||||
│ │
|
||||
│ Uso no componente: │
|
||||
│ const { isEnabled } = useTenantFeatures(); │
|
||||
│ if (isEnabled("bi")) { /* mostra menu BI */ } │
|
||||
│ if (isEnabled("retail")) { /* mostra módulo varejo */ } │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Interface de Administração
|
||||
|
||||
### 8.1 — Admin.tsx (Página Principal)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Admin.tsx — client/src/pages/Admin.tsx (2.635 linhas) │
|
||||
│ Rota: /admin │
|
||||
│ │
|
||||
│ 6 Abas: │
|
||||
│ ┌─────────────┬──────────┬────────────┬───────────┬──────────────┐ │
|
||||
│ │ Conexões │ Tarefas │ Knowledge │ Bibliotecas│ Módulos │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ WhatsApp │ Dev │ Knowledge │ Conteúdo │ Ativação │ │
|
||||
│ │ ERPNext │ Center │ Graph │ Docs │ por tenant │ │
|
||||
│ │ APIs │ Pipeline │ Q&A │ Templates │ Toggle on/ │ │
|
||||
│ │ │ │ │ │ off módulos │ │
|
||||
│ └─────────────┴──────────┴────────────┴───────────┴──────────────┘ │
|
||||
│ ┌─────────────┐ │
|
||||
│ │Casa de │ │
|
||||
│ │Máquinas │ │
|
||||
│ │ │ │
|
||||
│ │ Status dos │ │
|
||||
│ │ motores │ │
|
||||
│ │ (:8002-8006)│ │
|
||||
│ └─────────────┘ │
|
||||
│ │
|
||||
│ Aba "Módulos" renderiza: <MultiTenantSection /> │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 8.2 — MultiTenantSection (Gestão Completa)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ MultiTenantSection.tsx (1.486 linhas) │
|
||||
│ client/src/components/MultiTenantSection.tsx │
|
||||
│ │
|
||||
│ 4 Abas internas: │
|
||||
│ │
|
||||
│ ┌─ Tenants ──────────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ Lista de tenants com: │ │
|
||||
│ │ • Nome, tipo (Master/Parceiro/Cliente) │ │
|
||||
│ │ • Plano atual e status │ │
|
||||
│ │ • Nº de filhos (childCount) │ │
|
||||
│ │ • Tenant pai (parentTenant) │ │
|
||||
│ │ • Ações: Editar, Ativar/Desativar módulos │ │
|
||||
│ │ │ │
|
||||
│ │ Formulário de criação: │ │
|
||||
│ │ • Nome, email, phone │ │
|
||||
│ │ • Tipo (master/partner/client) │ │
|
||||
│ │ • Plano (seleciona do tenant_plans) │ │
|
||||
│ │ • Tenant pai (para hierarquia) │ │
|
||||
│ │ • Features auto-preenchidas pelo plano selecionado │ │
|
||||
│ │ • Toggle individual de cada módulo │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─ Planos ───────────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ CRUD de planos com: │ │
|
||||
│ │ • Código, nome, descrição │ │
|
||||
│ │ • Tipo de tenant (client/partner) │ │
|
||||
│ │ • Preço mensal e anual (centavos) │ │
|
||||
│ │ • Max usuários e storage │ │
|
||||
│ │ • Features (toggle de cada módulo) │ │
|
||||
│ │ • Botão "Seed" para criar planos padrão │ │
|
||||
│ │ • Botão "Propagar" para aplicar features do plano │ │
|
||||
│ │ a todos os tenants que usam esse plano │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─ Comissões ────────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ Lista de comissões de parceiros: │ │
|
||||
│ │ • Parceiro, Cliente, Mês referência │ │
|
||||
│ │ • Valor do plano, taxa de comissão, valor da comissão │ │
|
||||
│ │ • Status (pending → approved → paid) │ │
|
||||
│ │ • Ações: Aprovar, Marcar como paga │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─ Empresas ─────────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ EmpresasSubSection: │ │
|
||||
│ │ • Lista de empresas (Matriz/Filiais) por tenant │ │
|
||||
│ │ • CNPJ, Razão Social, Nome Fantasia │ │
|
||||
│ │ • IE, IM, Regime Tributário │ │
|
||||
│ │ • Endereço completo (CEP, logradouro, cidade, UF) │ │
|
||||
│ │ • Config Fiscal (ambiente, série NF-e, série NFC-e) │ │
|
||||
│ │ • Link com ERP Plus (plusEmpresaId) │ │
|
||||
│ │ • Ações: Criar, Editar, Excluir │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. API REST Completa (/api/admin/*)
|
||||
|
||||
### Tenants
|
||||
|
||||
| Método | Endpoint | Função | Acesso |
|
||||
|--------|----------|--------|--------|
|
||||
| GET | `/tenants` | Lista tenants (filtrado por acesso) | master/partner/client |
|
||||
| POST | `/tenants` | Cria tenant (resolve features do plano) | master/admin |
|
||||
| PATCH | `/tenants/:id` | Atualiza tenant (canAccessTenant) | master/partner |
|
||||
| DELETE | `/tenants/:id` | Exclui tenant (proteção master) | somente master |
|
||||
| GET | `/tenants/hierarchy` | Árvore hierárquica de tenants | master/partner |
|
||||
| GET | `/tenants/stats` | Estatísticas (total, por tipo, por status) | master/admin |
|
||||
|
||||
### Planos
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/plans` | Lista todos os planos |
|
||||
| POST | `/plans` | Cria novo plano |
|
||||
| PATCH | `/plans/:id` | Atualiza plano |
|
||||
| DELETE | `/plans/:id` | Exclui plano |
|
||||
| POST | `/plans/:id/propagate` | Propaga features do plano para todos os tenants que o usam |
|
||||
| POST | `/plans/seed` | Cria 6 planos padrão |
|
||||
|
||||
### Parceiro-Cliente
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/partner-clients` | Lista relacionamentos parceiro↔cliente |
|
||||
| POST | `/partner-clients` | Cria vínculo parceiro↔cliente |
|
||||
|
||||
### Comissões de Parceiros
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/commissions` | Lista comissões (filtro por partnerId, status) |
|
||||
| PATCH | `/commissions/:id/approve` | Aprova comissão |
|
||||
| PATCH | `/commissions/:id/pay` | Marca como paga |
|
||||
|
||||
### Empresas (Matriz/Filiais)
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/empresas` | Lista empresas (filtro por tenantId + acesso) |
|
||||
| GET | `/empresas/:id` | Detalhe da empresa |
|
||||
| POST | `/empresas` | Cria empresa (verifica canAccessTenant) |
|
||||
| PATCH | `/empresas/:id` | Atualiza empresa |
|
||||
| DELETE | `/empresas/:id` | Exclui empresa |
|
||||
|
||||
### Outros Admin
|
||||
|
||||
| Método | Endpoint | Função |
|
||||
|--------|----------|--------|
|
||||
| GET | `/profiles` | Lista perfis de usuários |
|
||||
| POST | `/profiles` | Cria perfil |
|
||||
| PATCH | `/profiles/:id` | Atualiza perfil |
|
||||
| DELETE | `/profiles/:id` | Exclui perfil |
|
||||
| GET | `/users` | Lista usuários (com tenant info) |
|
||||
| PATCH | `/users/:id` | Atualiza usuário |
|
||||
| PATCH | `/users/:id/status` | Altera status do usuário |
|
||||
| PATCH | `/partners/:id/status` | Altera status de parceiro |
|
||||
| PATCH | `/clients/:id/status` | Altera status de cliente |
|
||||
| GET | `/stats` | Estatísticas gerais (tenants, users, leads) |
|
||||
| GET | `/libraries` | Lista bibliotecas de conteúdo |
|
||||
|
||||
**Total: 33 endpoints de administração**
|
||||
|
||||
---
|
||||
|
||||
## 10. Fluxo Completo — Onboarding de Tenant
|
||||
|
||||
```
|
||||
┌──────────┐ ┌────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ 1. CRIAR │───▶│ 2. PLANO │───▶│ 3. FEATURES │───▶│ 4. EMPRESAS │
|
||||
│ TENANT │ │ │ │ │ │ │
|
||||
│ │ │ Seleciona │ │ Auto-resolve │ │ Cadastra │
|
||||
│ POST │ │ plano │ │ do plano ou │ │ Matriz │
|
||||
│ /admin/ │ │ (free, │ │ toggle │ │ + Filiais │
|
||||
│ tenants │ │ starter, │ │ individual │ │ │
|
||||
│ │ │ pro, etc) │ │ de módulos │ │ CNPJ, IE, │
|
||||
│ name, │ │ │ │ │ │ endereço, │
|
||||
│ email, │ │ maxUsers │ │ ide: true │ │ regime │
|
||||
│ type │ │ maxStorage │ │ crm: true │ │ tributário, │
|
||||
│ │ │ preço │ │ bi: false │ │ certificado │
|
||||
│ │ │ │ │ ... │ │ digital │
|
||||
└──────────┘ └────────────┘ └──────────────┘ └──────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐
|
||||
│ 5. VINCULAR │ │ 6. COMISSÕES │ │ 7. USUÁRIOS │
|
||||
│ PARCEIRO │ │ │ │ │
|
||||
│ (se partner) │ │ Automáticas │ │ Convida users │
|
||||
│ │ │ mensais │ │ tenant_users │
|
||||
│ POST /admin/ │ │ baseadas no │ │ roles: owner/admin/ │
|
||||
│ partner- │ │ plano do │ │ member │
|
||||
│ clients │ │ cliente │ │ │
|
||||
│ │ │ │ │ Scoping automático │
|
||||
│ partnerId │ │ pending → │ │ de todos os dados │
|
||||
│ clientId │ │ approved → │ │ por tenantId │
|
||||
│ commission% │ │ paid │ │ │
|
||||
└──────────────┘ └──────────────┘ └──────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Propagação de Plano
|
||||
|
||||
Quando um plano é atualizado, a feature de **propagação** atualiza todos os tenants que usam esse plano:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ POST /api/admin/plans/:id/propagate │
|
||||
│ │
|
||||
│ 1. Busca o plano por ID │
|
||||
│ 2. Pega features, maxUsers, maxStorageMb do plano │
|
||||
│ 3. Atualiza TODOS os tenants onde plan === plan.code: │
|
||||
│ UPDATE tenants SET │
|
||||
│ features = plan.features, │
|
||||
│ maxUsers = plan.maxUsers, │
|
||||
│ maxStorageMb = plan.maxStorageMb │
|
||||
│ WHERE plan = 'pro' │
|
||||
│ 4. Retorna: { propagated: 15, planCode: "pro" } │
|
||||
│ │
|
||||
│ Exemplo: │
|
||||
│ Plano "pro" ganha módulo "production" → │
|
||||
│ Propagar → 15 tenants "pro" recebem production: true │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Isolamento de Dados
|
||||
|
||||
Toda query de dados respeita o escopo do tenant:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ISOLAMENTO POR tenantId │
|
||||
│ │
|
||||
│ Padrão em todas as queries: │
|
||||
│ │
|
||||
│ const tenantId = (req.user as any).tenantId; │
|
||||
│ const data = await db.select() │
|
||||
│ .from(tabela) │
|
||||
│ .where(eq(tabela.tenantId, tenantId)); │
|
||||
│ │
|
||||
│ Tabelas com tenantId: │
|
||||
│ • tenants (hierarquia) • crm_channels │
|
||||
│ • tenant_empresas • crm_threads │
|
||||
│ • crm_leads • crm_events │
|
||||
│ • crm_clients • crm_campaigns │
|
||||
│ • crm_opportunities • crm_products │
|
||||
│ • crm_partners • crm_contracts │
|
||||
│ • crm_pipeline_stages • crm_commission_rules │
|
||||
│ • crm_proposals • crm_quick_messages │
|
||||
│ • retail_activity_feed • valuation_* │
|
||||
│ │
|
||||
│ Resultado: Dados do Tenant A NUNCA são vistos pelo Tenant B │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. Arquivos-Chave
|
||||
|
||||
| Arquivo | Função | Linhas |
|
||||
|---------|--------|--------|
|
||||
| `shared/schema.ts` (L821-977) | TenantFeatures type + 6 tabelas tenant | ~160 |
|
||||
| `server/admin/routes.ts` | API admin (33 endpoints) | 876 |
|
||||
| `client/src/pages/Admin.tsx` | Página admin (6 abas) | 2.635 |
|
||||
| `client/src/components/MultiTenantSection.tsx` | Gestão multi-tenant (4 sub-abas) | 1.486 |
|
||||
| `client/src/hooks/use-tenant-features.ts` | Hook de features no frontend | 59 |
|
||||
| `server/retail/routes.ts` (L25) | Middleware requireModule | ~25 |
|
||||
| `server/storage.ts` (getEnrichedUser) | Enriquece user com tenant info | — |
|
||||
|
||||
---
|
||||
|
||||
## 14. Resumo Visual
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ARCÁDIA MULTI-TENANT STACK │
|
||||
│ │
|
||||
│ ┌───── Autenticação ──────────────────────────────────────────┐ │
|
||||
│ │ Login → getEnrichedUser → user + tenantId + tenantType │ │
|
||||
│ │ + tenantRole + features │ │
|
||||
│ └─────────────────────────────────────────┬───────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────────────────────────▼───────────────────┐ │
|
||||
│ │ Middleware │ │
|
||||
│ │ ┌──────────┐ ┌─────────────┐ ┌────────────────────┐ │ │
|
||||
│ │ │requireAuth│ │requireModule │ │getAllowedTenantIds │ │ │
|
||||
│ │ │role check │ │feature check │ │canAccessTenant │ │ │
|
||||
│ │ └──────────┘ └─────────────┘ └────────────────────┘ │ │
|
||||
│ └─────────────────────────────────────────┬───────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────────────────────────▼───────────────────┐ │
|
||||
│ │ API /api/admin/* │ │
|
||||
│ │ 33 endpoints: Tenants, Plans, Empresas, Comissões │ │
|
||||
│ └─────────────────────────────────────────┬───────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────────────────────────────▼───────────────────┐ │
|
||||
│ │ PostgreSQL │ │
|
||||
│ │ 6 tabelas: tenants, tenant_users, tenant_plans, │ │
|
||||
│ │ tenant_empresas, partner_clients, partner_commissions │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───── Frontend ──────────────────────────────────────────────┐ │
|
||||
│ │ Admin.tsx (6 abas) → MultiTenantSection (4 sub-abas) │ │
|
||||
│ │ useTenantFeatures() hook → GET /api/soe/tenant/modules │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
|
@ -0,0 +1,733 @@
|
|||
# PLANO SOE CENTRAL — Arcádia Suite
|
||||
## Orquestração Inteligente: Plus + ERPNext invisíveis, Central visível
|
||||
### Versão 1.0 — Março 2026
|
||||
|
||||
---
|
||||
|
||||
## 1. ONDE ESTAMOS (Orientação Geral)
|
||||
|
||||
### O Projeto nasceu no Replit como…
|
||||
- Uma plataforma de **escritório empresarial com IA central** (Manus Agent)
|
||||
- Com proxy para o **Arcádia Plus** (Laravel/PHP, porta 8080) — ERP fiscal completo
|
||||
- Com integração ao **ERPNext/Frappe** via API
|
||||
- Stack principal: **React 18 + Express.js + PostgreSQL + FastAPI (Python)**
|
||||
|
||||
### O que JÁ existe e funciona:
|
||||
| Componente | Status | Onde vive |
|
||||
|---|---|---|
|
||||
| Manus Agent (IA, 30+ tools) | ✅ Funcionando | `server/manus/` |
|
||||
| Arcádia Plus (Laravel ERP) | ✅ Funcionando | `plus/` → porta 8080 |
|
||||
| SSO Plus ↔ Suite | ✅ Funcionando | `server/plus/` |
|
||||
| Motor Fiscal (nfelib) | ✅ Funcionando | `server/python/` → porta 8002 |
|
||||
| Motor Contábil | ✅ Funcionando | `server/python/` → porta 8003 |
|
||||
| Motor BI | ✅ Funcionando | `server/python/` → porta 8004 |
|
||||
| Motor Automação | ✅ Funcionando | `server/python/` → porta 8005 |
|
||||
| Motor Comunicação (Comm) | ✅ Funcionando | porta 8006 |
|
||||
| Dev Center XOS (6 agentes) | ✅ Funcionando | `server/blackboard/` |
|
||||
| LiteLLM Gateway | ✅ Configurado | porta 4000 |
|
||||
| CRM, WhatsApp, Chat | ✅ Funcionando | `server/crm/`, `server/whatsapp/` |
|
||||
| ERPNext integração | ⚠️ Parcial | `server/erp/routes.ts` |
|
||||
|
||||
### O que FALTA para o plano atual:
|
||||
| Componente | Status |
|
||||
|---|---|
|
||||
| **SOE Central (Regras Unificadas)** | ❌ Não existe — este é o plano |
|
||||
| Fiscal rules centralizadas (acima do Plus) | ❌ Dispersas no Plus |
|
||||
| ERPNext rules centralizadas | ❌ Não existe camada de abstração |
|
||||
| Contábil Avançado (lançamentos automáticos) | ❌ Motor existe, regras não |
|
||||
| Financeiro Avançado (previsão, DRE automático) | ❌ Motor existe, regras não |
|
||||
| Testes automatizados / CI-CD | ❌ |
|
||||
| Monitoramento (APM, Sentry) | ❌ |
|
||||
|
||||
---
|
||||
|
||||
## 2. O PROBLEMA QUE VAMOS RESOLVER
|
||||
|
||||
### Situação Atual (problemática)
|
||||
```
|
||||
Usuário → Arcádia Suite
|
||||
├── /plus → acessa Laravel diretamente (usuário VÊ o Plus)
|
||||
├── /erp → acessa ERPNext diretamente (usuário VÊ o ERPNext)
|
||||
└── Regras fiscais ficam dentro do Plus
|
||||
Regras contábeis ficam no Motor 8003
|
||||
Regras ERP ficam no ERPNext
|
||||
→ Fragmentado, sem governança central
|
||||
```
|
||||
|
||||
### Situação Desejada (SOE Central)
|
||||
```
|
||||
Usuário → Arcádia Suite (SOE)
|
||||
│
|
||||
▼ [Central de Regras SOE]
|
||||
├── Fiscal: regras unificadas (CFOP, NCM, tributação, NF-e, CT-e...)
|
||||
├── ERP: regras de negócio (pedidos, estoque, compras...)
|
||||
├── Contábil: regras de lançamentos, plano de contas, DRE...
|
||||
└── Financeiro: regras de fluxo, provisões, conciliação...
|
||||
│
|
||||
▼ [Execução invisível]
|
||||
├── Plus → emite documentos, registra no banco
|
||||
└── ERPNext → executa operações, registra no banco
|
||||
|
||||
O usuário NUNCA VÊ o Plus nem o ERPNext.
|
||||
Só vê a Central SOE do Arcádia.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. ARQUITETURA SOE CENTRAL
|
||||
|
||||
### 3.1 Diagrama Completo
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ ARCÁDIA SUITE (Frontend React) │
|
||||
│ /financeiro /contabil /fisco /erp /people /production /bi │
|
||||
│ Usuário nunca acessa /plus ou ERPNext diretamente │
|
||||
└───────────────────────────────┬─────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ SOE — SISTEMA OPERACIONAL EMPRESARIAL (Nova Camada) │
|
||||
│ Express.js / porta 5000 / server/soe/ │
|
||||
│ │
|
||||
│ ┌──────────────────────┐ ┌──────────────────────┐ │
|
||||
│ │ CENTRAL FISCAL │ │ CENTRAL ERP │ │
|
||||
│ │ server/soe/fiscal/ │ │ server/soe/erp/ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ • Regras NCM/CFOP │ │ • Regras de pedidos │ │
|
||||
│ │ • Políticas tribut. │ │ • Regras de estoque │ │
|
||||
│ │ • Emissão NF-e/NFC-e │ │ • Regras de compras │ │
|
||||
│ │ • Cancelamento │ │ • Regras de pessoas │ │
|
||||
│ │ • SPED/SINTEGRA │ │ • Integrações ext. │ │
|
||||
│ └──────────┬───────────┘ └──────────┬─────────────┘ │
|
||||
│ │ │ │
|
||||
│ ┌──────────────────────┐ ┌──────────────────────┐ │
|
||||
│ │ CENTRAL CONTÁBIL │ │ CENTRAL FINANCEIRO │ │
|
||||
│ │ server/soe/contabil/│ │ server/soe/financ/ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ • Plano de contas │ │ • Fluxo de caixa │ │
|
||||
│ │ • Regras de lanç. │ │ • Contas a P/R │ │
|
||||
│ │ • DRE automático │ │ • Conciliação │ │
|
||||
│ │ • Balancete │ │ • Previsão (IA) │ │
|
||||
│ │ • Fechamento │ │ • Impostos │ │
|
||||
│ └──────────┬───────────┘ └──────────┬─────────────┘ │
|
||||
│ │ │ │
|
||||
│ └────────────┬─────────────┘ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────┐ │
|
||||
│ │ SOE RULE ENGINE (Núcleo) │ │
|
||||
│ │ server/soe/rule-engine.ts │ │
|
||||
│ │ │ │
|
||||
│ │ • Resolução de conflitos │ │
|
||||
│ │ • Aplicação de políticas XOS │ │
|
||||
│ │ • Audit trail de todas regras │ │
|
||||
│ │ • Manus consulta este engine │ │
|
||||
│ └─────────────────┬───────────────┘ │
|
||||
└────────────────────────────┼────────────────────────────────────────────┘
|
||||
│
|
||||
┌──────────────┼───────────────┐
|
||||
▼ ▼ ▼
|
||||
┌─────────────────┐ ┌──────────────┐ ┌──────────────────┐
|
||||
│ ARCÁDIA PLUS │ │ ERPNEXT │ │ MOTORES PYTHON │
|
||||
│ Laravel :8080 │ │ Frappe : │ │ 8002 Fiscal │
|
||||
│ (invisível) │ │ (invisível) │ │ 8003 Contábil │
|
||||
│ │ │ │ │ 8004 BI │
|
||||
│ Emite docs │ │ Executa ERP │ │ 8005 Automação │
|
||||
│ fiscais │ │ operacional │ │ │
|
||||
└─────────────────┘ └──────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. CENTRAL FISCAL — Detalhamento
|
||||
|
||||
### 4.1 O que ela resolve
|
||||
O Plus tem **376 controllers** e **1.546 rotas** — tudo fiscal/operacional. A Central Fiscal **não substitui** o Plus, ela **orquestra** o Plus com regras definidas no SOE.
|
||||
|
||||
### 4.2 Regras que vão para a Central (tiradas do Plus)
|
||||
|
||||
```typescript
|
||||
// server/soe/fiscal/rules.ts — Exemplo de estrutura
|
||||
|
||||
interface FiscalRule {
|
||||
id: string
|
||||
nome: string
|
||||
trigger: 'pre_emissao' | 'pos_emissao' | 'cancelamento' | 'validacao'
|
||||
condicao: FiscalCondition
|
||||
acao: FiscalAction
|
||||
prioridade: number
|
||||
ativo: boolean
|
||||
}
|
||||
|
||||
// Exemplos de regras:
|
||||
const REGRAS_FISCAIS: FiscalRule[] = [
|
||||
{
|
||||
id: 'NFE_CFOP_INTERESTADUAL',
|
||||
nome: 'Ajusta CFOP para operação interestadual',
|
||||
trigger: 'pre_emissao',
|
||||
condicao: { uf_emitente: '!=', uf_destinatario: true },
|
||||
acao: { ajustar_cfop: 'interestadual' },
|
||||
prioridade: 1,
|
||||
ativo: true
|
||||
},
|
||||
{
|
||||
id: 'NFE_SIMPLES_NACIONAL',
|
||||
nome: 'Aplica tributação Simples Nacional',
|
||||
trigger: 'pre_emissao',
|
||||
condicao: { regime_tributario: 'simples_nacional' },
|
||||
acao: { aplicar_cst: 'tabela_simples', remover_pis_cofins: true },
|
||||
prioridade: 2,
|
||||
ativo: true
|
||||
},
|
||||
{
|
||||
id: 'NFE_VALIDACAO_NCM',
|
||||
nome: 'Valida NCM obrigatório para NF-e',
|
||||
trigger: 'validacao',
|
||||
condicao: { tipo_documento: 'nfe' },
|
||||
acao: { validar_campo: 'ncm', obrigatorio: true },
|
||||
prioridade: 1,
|
||||
ativo: true
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 4.3 Fluxo de Emissão via SOE Central
|
||||
|
||||
```
|
||||
Usuário clica "Emitir NF-e" no Arcádia
|
||||
│
|
||||
▼
|
||||
POST /api/soe/fiscal/emitir-nfe
|
||||
│
|
||||
▼
|
||||
SOE Rule Engine: aplica REGRAS_FISCAIS (pre_emissao)
|
||||
• Verifica regime tributário
|
||||
• Aplica CFOP correto
|
||||
• Calcula impostos (ICMS, PIS, COFINS, IPI)
|
||||
• Valida NCM obrigatório
|
||||
• Adiciona dados da empresa emitente
|
||||
│
|
||||
▼
|
||||
SOE envia para Plus via API interna (invisível)
|
||||
POST http://plus:8080/api/nfe/emitir
|
||||
+ token SSO automático
|
||||
│
|
||||
▼
|
||||
Plus emite via Cloud-DFE → SEFAZ
|
||||
│
|
||||
▼
|
||||
SOE recebe retorno
|
||||
• Registra no banco SOE (tabela soe_fiscal_eventos)
|
||||
• Dispara lançamento contábil automático
|
||||
• Notifica via WebSocket o frontend
|
||||
│
|
||||
▼
|
||||
Usuário vê resultado no Arcádia (nunca soube do Plus)
|
||||
```
|
||||
|
||||
### 4.4 Tabelas SOE para Fiscal
|
||||
|
||||
```sql
|
||||
-- Tabelas a criar no schema Arcádia (PostgreSQL)
|
||||
|
||||
CREATE TABLE soe_fiscal_regras (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
nome VARCHAR(200) NOT NULL,
|
||||
trigger VARCHAR(50) NOT NULL,
|
||||
condicao JSONB NOT NULL,
|
||||
acao JSONB NOT NULL,
|
||||
prioridade INTEGER DEFAULT 10,
|
||||
ativo BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE soe_fiscal_eventos (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tipo VARCHAR(50) NOT NULL, -- 'emissao_nfe', 'cancelamento', etc.
|
||||
empresa_id INTEGER NOT NULL,
|
||||
documento_chave VARCHAR(100),
|
||||
regras_aplicadas JSONB, -- quais regras foram aplicadas
|
||||
payload_enviado JSONB, -- o que foi enviado ao Plus
|
||||
payload_retorno JSONB, -- o que o Plus/SEFAZ respondeu
|
||||
status VARCHAR(50) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE soe_fiscal_configuracoes (
|
||||
empresa_id INTEGER PRIMARY KEY,
|
||||
regime_tributario VARCHAR(50), -- simples, lucro_presumido, lucro_real
|
||||
uf VARCHAR(2),
|
||||
cnpj VARCHAR(18),
|
||||
certificado_tipo VARCHAR(20),
|
||||
aliquotas JSONB, -- alíquotas padrão por operação
|
||||
cfops_padrao JSONB, -- CFOPs padrão por tipo de operação
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. CENTRAL ERP (ERPNext/Frappe) — Detalhamento
|
||||
|
||||
### 5.1 O que ela resolve
|
||||
O ERPNext tem **APIs complexas**. A Central ERP abstrai tudo isso em operações simples que o Arcádia entende.
|
||||
|
||||
### 5.2 Adapter Layer ERPNext
|
||||
|
||||
```typescript
|
||||
// server/soe/erp/frappe-adapter.ts
|
||||
|
||||
interface ERPAdapter {
|
||||
// Produtos
|
||||
criarProduto(data: ProdutoSOE): Promise<ERPProduto>
|
||||
buscarProduto(codigo: string): Promise<ProdutoSOE>
|
||||
atualizarEstoque(movimentacao: MovimentacaoEstoque): Promise<void>
|
||||
|
||||
// Pedidos
|
||||
criarPedidoVenda(pedido: PedidoSOE): Promise<ERPPedido>
|
||||
aprovarPedido(id: string): Promise<void>
|
||||
faturarPedido(id: string): Promise<ERPFatura>
|
||||
|
||||
// Financeiro
|
||||
criarLancamento(lancamento: LancamentoSOE): Promise<ERPLancamento>
|
||||
buscarPlanoContas(): Promise<ContaContabil[]>
|
||||
|
||||
// Compras
|
||||
criarPedidoCompra(data: PedidoCompraSOE): Promise<ERPPedidoCompra>
|
||||
receberMercadoria(data: RecebimentoSOE): Promise<void>
|
||||
}
|
||||
|
||||
// As regras ficam no SOE, o adapter só traduz
|
||||
class FrappeAdapter implements ERPAdapter {
|
||||
private baseUrl: string
|
||||
private apiKey: string
|
||||
|
||||
async criarPedidoVenda(pedido: PedidoSOE): Promise<ERPPedido> {
|
||||
// Traduz do modelo SOE para o modelo Frappe
|
||||
const frappePedido = this.traduzirParaFrappe(pedido)
|
||||
|
||||
// Envia para ERPNext (invisível para o usuário)
|
||||
const response = await fetch(`${this.baseUrl}/api/resource/Sales Order`, {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': `token ${this.apiKey}` },
|
||||
body: JSON.stringify(frappePedido)
|
||||
})
|
||||
|
||||
// Traduz retorno de volta para modelo SOE
|
||||
return this.traduzirDeVoltaSOE(await response.json())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 Regras de Negócio ERP no SOE
|
||||
|
||||
```typescript
|
||||
// server/soe/erp/rules.ts
|
||||
|
||||
const REGRAS_ERP = [
|
||||
{
|
||||
id: 'PEDIDO_APROVACAO_AUTOMATICA',
|
||||
nome: 'Aprovação automática até R$ 5.000',
|
||||
trigger: 'pre_aprovacao_pedido',
|
||||
condicao: { valor_total: { menor_que: 5000 } },
|
||||
acao: { aprovar_automaticamente: true }
|
||||
},
|
||||
{
|
||||
id: 'ESTOQUE_RESERVA_VENDA',
|
||||
nome: 'Reservar estoque ao confirmar venda',
|
||||
trigger: 'pos_confirmacao_venda',
|
||||
condicao: { status: 'confirmado' },
|
||||
acao: { reservar_estoque: true, gerar_separacao: true }
|
||||
},
|
||||
{
|
||||
id: 'NF_AUTOMATICA_FATURAMENTO',
|
||||
nome: 'Emite NF-e automaticamente ao faturar',
|
||||
trigger: 'pos_faturamento',
|
||||
condicao: { tipo_cliente: ['pj', 'pf_contribuinte'] },
|
||||
acao: { emitir_nfe: true, via_soe_fiscal: true }
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. CENTRAL CONTÁBIL AVANÇADA
|
||||
|
||||
### 6.1 Motor Atual (porta 8003) + Regras SOE
|
||||
|
||||
O Motor Contábil Python já faz DRE e Balancete. O que falta é **alimentação automática**.
|
||||
|
||||
### 6.2 Lançamentos Automáticos (grande salto)
|
||||
|
||||
```typescript
|
||||
// server/soe/contabil/auto-lancamentos.ts
|
||||
|
||||
interface RegrasLancamentoAuto {
|
||||
gatilho: string
|
||||
debito: string // Conta contábil
|
||||
credito: string // Conta contábil
|
||||
historico: string
|
||||
formula: string // Como calcular o valor
|
||||
}
|
||||
|
||||
const LANCAMENTOS_AUTOMATICOS: RegrasLancamentoAuto[] = [
|
||||
// Quando emite NF-e de venda
|
||||
{
|
||||
gatilho: 'emissao_nfe_venda',
|
||||
debito: '1.1.3.01', // Clientes
|
||||
credito: '3.1.1.01', // Receita Bruta de Vendas
|
||||
historico: 'Venda conforme NF-e {numero}',
|
||||
formula: 'valor_total_nota'
|
||||
},
|
||||
{
|
||||
gatilho: 'emissao_nfe_venda',
|
||||
debito: '3.1.2.01', // Dedução de Receita (ICMS)
|
||||
credito: '2.1.2.01', // ICMS a Recolher
|
||||
historico: 'ICMS s/ Venda NF-e {numero}',
|
||||
formula: 'valor_icms'
|
||||
},
|
||||
// Quando paga conta
|
||||
{
|
||||
gatilho: 'pagamento_conta',
|
||||
debito: '2.1.1.{categoria}', // Conta a Pagar específica
|
||||
credito: '1.1.1.01', // Banco/Caixa
|
||||
historico: 'Pagamento: {descricao}',
|
||||
formula: 'valor_pago'
|
||||
},
|
||||
// Quando recebe pagamento
|
||||
{
|
||||
gatilho: 'recebimento',
|
||||
debito: '1.1.1.01', // Banco/Caixa
|
||||
credito: '1.1.3.01', // Clientes
|
||||
historico: 'Recebimento: {descricao}',
|
||||
formula: 'valor_recebido'
|
||||
},
|
||||
// Compra de mercadoria
|
||||
{
|
||||
gatilho: 'entrada_nfe_compra',
|
||||
debito: '1.1.4.01', // Estoque
|
||||
credito: '2.1.1.01', // Fornecedores
|
||||
historico: 'Compra conforme NF-e {numero} / {fornecedor}',
|
||||
formula: 'valor_mercadorias'
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 6.3 DRE Automático via SOE
|
||||
|
||||
```
|
||||
Usuário pede DRE do mês
|
||||
│
|
||||
▼
|
||||
SOE Contábil consulta:
|
||||
1. Todos lançamentos do período (tabela soe_lancamentos)
|
||||
2. Agrupa por conta contábil
|
||||
3. Aplica estrutura DRE configurada
|
||||
4. Envia para Motor Python 8003 para cálculos
|
||||
5. Retorna DRE formatado
|
||||
│
|
||||
▼
|
||||
Usuário vê DRE no /contabil do Arcádia
|
||||
(Nunca soube que teve lançamentos automáticos de NF-e, pagamentos, etc.)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. CENTRAL FINANCEIRO AVANÇADO
|
||||
|
||||
### 7.1 Regras Financeiras no SOE
|
||||
|
||||
```typescript
|
||||
// server/soe/financeiro/rules.ts
|
||||
|
||||
const REGRAS_FINANCEIRO = [
|
||||
{
|
||||
id: 'BOLETO_AUTO_VENCIMENTO',
|
||||
nome: 'Gerar boleto automático para vendas a prazo',
|
||||
trigger: 'pos_confirmacao_venda_prazo',
|
||||
acao: { gerar_boleto: true, via: 'asaas', vencimento: '+{prazo}_dias' }
|
||||
},
|
||||
{
|
||||
id: 'ALERTA_VENCIMENTO_3DIAS',
|
||||
nome: 'Alerta de vencimento 3 dias antes',
|
||||
trigger: 'cron_diario',
|
||||
condicao: { vencimento_em: 3 },
|
||||
acao: { notificar: ['whatsapp', 'email'], via_manus: true }
|
||||
},
|
||||
{
|
||||
id: 'PROVISAO_IMPOSTOS',
|
||||
nome: 'Provisão mensal de impostos',
|
||||
trigger: 'cron_mensal',
|
||||
acao: { calcular_provisao: ['icms', 'pis', 'cofins', 'csll', 'irpj'], registrar_lancamento: true }
|
||||
},
|
||||
{
|
||||
id: 'CONCILIACAO_AUTO',
|
||||
nome: 'Conciliação bancária automática via OFX',
|
||||
trigger: 'importacao_ofx',
|
||||
acao: { tentar_conciliar: true, confianca_minima: 0.85, pendentes_para_revisao: true }
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. MANUS AGENT + SOE CENTRAL
|
||||
|
||||
### 8.1 O Manus consulta a Central
|
||||
|
||||
```
|
||||
Pergunta: "Qual foi o resultado do mês passado?"
|
||||
│
|
||||
▼
|
||||
Manus → SOE Central
|
||||
• GET /api/soe/contabil/dre?periodo=2026-02
|
||||
• GET /api/soe/financeiro/fluxo?periodo=2026-02
|
||||
• GET /api/soe/fiscal/resumo?periodo=2026-02
|
||||
│
|
||||
▼
|
||||
SOE agrega dados dos motores (Python) + Plus + ERPNext
|
||||
│
|
||||
▼
|
||||
Manus recebe contexto completo e responde:
|
||||
"Faturamento: R$ 120.000
|
||||
Deduções (impostos): R$ 18.000
|
||||
Receita Líquida: R$ 102.000
|
||||
Custo das mercadorias: R$ 65.000
|
||||
Lucro Bruto: R$ 37.000
|
||||
..."
|
||||
```
|
||||
|
||||
### 8.2 Tools do Manus para o SOE
|
||||
|
||||
```typescript
|
||||
// Novas tools do Manus que consultam o SOE Central
|
||||
|
||||
{
|
||||
name: 'consultar_situacao_fiscal',
|
||||
description: 'Consulta situação fiscal da empresa (NFs pendentes, impostos, SPED)',
|
||||
endpoint: 'GET /api/soe/fiscal/situacao'
|
||||
},
|
||||
{
|
||||
name: 'emitir_documento_fiscal',
|
||||
description: 'Emite NF-e, NFC-e, CT-e ou MDF-e',
|
||||
endpoint: 'POST /api/soe/fiscal/emitir'
|
||||
},
|
||||
{
|
||||
name: 'consultar_dre',
|
||||
description: 'Gera DRE de qualquer período',
|
||||
endpoint: 'GET /api/soe/contabil/dre'
|
||||
},
|
||||
{
|
||||
name: 'consultar_fluxo_caixa',
|
||||
description: 'Consulta fluxo de caixa e previsões',
|
||||
endpoint: 'GET /api/soe/financeiro/fluxo'
|
||||
},
|
||||
{
|
||||
name: 'registrar_lancamento_contabil',
|
||||
description: 'Registra lançamento contábil manualmente',
|
||||
endpoint: 'POST /api/soe/contabil/lancamento'
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. ESTRUTURA DE PASTAS A CRIAR
|
||||
|
||||
```
|
||||
server/
|
||||
└── soe/ ← Nova pasta (Central SOE)
|
||||
├── index.ts ← Router principal do SOE
|
||||
├── rule-engine.ts ← Motor de regras central
|
||||
│
|
||||
├── fiscal/
|
||||
│ ├── routes.ts ← GET/POST /api/soe/fiscal/*
|
||||
│ ├── rules.ts ← Regras fiscais (CFOP, NCM, tributação)
|
||||
│ ├── emissao.ts ← Orquestração de emissão (→ Plus)
|
||||
│ ├── plus-adapter.ts ← Adaptador para o Plus (invisível)
|
||||
│ └── sefaz-adapter.ts ← Comunicação direta SEFAZ (via Python 8002)
|
||||
│
|
||||
├── erp/
|
||||
│ ├── routes.ts ← GET/POST /api/soe/erp/*
|
||||
│ ├── rules.ts ← Regras de negócio ERP
|
||||
│ ├── frappe-adapter.ts ← Adaptador ERPNext (invisível)
|
||||
│ └── plus-erp-adapter.ts ← Adaptador Plus-ERP (para dados de venda)
|
||||
│
|
||||
├── contabil/
|
||||
│ ├── routes.ts ← GET/POST /api/soe/contabil/*
|
||||
│ ├── rules.ts ← Regras contábeis
|
||||
│ ├── auto-lancamentos.ts ← Lançamentos automáticos
|
||||
│ ├── plano-contas.ts ← Plano de contas SOE
|
||||
│ └── python-adapter.ts ← Adaptador Motor Python 8003
|
||||
│
|
||||
├── financeiro/
|
||||
│ ├── routes.ts ← GET/POST /api/soe/financeiro/*
|
||||
│ ├── rules.ts ← Regras financeiras
|
||||
│ ├── previsao.ts ← Previsão com IA
|
||||
│ └── conciliacao.ts ← Conciliação bancária
|
||||
│
|
||||
└── shared/
|
||||
├── types.ts ← Tipos SOE compartilhados
|
||||
├── audit.ts ← Registro de todas as operações
|
||||
└── event-bus.ts ← Eventos entre Centrais
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. TABELAS DO BANCO A CRIAR (Drizzle ORM)
|
||||
|
||||
```typescript
|
||||
// shared/schema.ts — Adicionar:
|
||||
|
||||
// Configuração da empresa no SOE
|
||||
export const soeEmpresaConfig = pgTable('soe_empresa_config', {
|
||||
id: serial('id').primaryKey(),
|
||||
empresaId: integer('empresa_id').notNull(),
|
||||
regimeTributario: varchar('regime_tributario', { length: 50 }), // simples, presumido, real
|
||||
uf: varchar('uf', { length: 2 }),
|
||||
cnpj: varchar('cnpj', { length: 18 }),
|
||||
planoContasId: integer('plano_contas_id'),
|
||||
configFiscal: jsonb('config_fiscal'), // alíquotas, CFOPs padrão
|
||||
configErp: jsonb('config_erp'), // qual ERP: plus, erpnext, ambos
|
||||
createdAt: timestamp('created_at').defaultNow(),
|
||||
})
|
||||
|
||||
// Regras configuráveis (editáveis pelo usuário)
|
||||
export const soeRegras = pgTable('soe_regras', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
empresaId: integer('empresa_id'), // null = regra global
|
||||
dominio: varchar('dominio', { length: 50 }).notNull(), // 'fiscal', 'erp', 'contabil', 'financeiro'
|
||||
nome: varchar('nome', { length: 200 }).notNull(),
|
||||
trigger: varchar('trigger', { length: 100 }).notNull(),
|
||||
condicao: jsonb('condicao').notNull(),
|
||||
acao: jsonb('acao').notNull(),
|
||||
prioridade: integer('prioridade').default(10),
|
||||
ativo: boolean('ativo').default(true),
|
||||
createdAt: timestamp('created_at').defaultNow(),
|
||||
})
|
||||
|
||||
// Log de todas as operações SOE (audit trail)
|
||||
export const soeEventos = pgTable('soe_eventos', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
empresaId: integer('empresa_id').notNull(),
|
||||
dominio: varchar('dominio', { length: 50 }).notNull(),
|
||||
tipo: varchar('tipo', { length: 100 }).notNull(),
|
||||
regraIds: jsonb('regra_ids'), // regras aplicadas
|
||||
payloadEntrada: jsonb('payload_entrada'),
|
||||
payloadSaida: jsonb('payload_saida'),
|
||||
erp: varchar('erp', { length: 50 }), // 'plus', 'erpnext', 'python_8003', etc.
|
||||
status: varchar('status', { length: 50 }).notNull(),
|
||||
duracao: integer('duracao'), // ms
|
||||
createdAt: timestamp('created_at').defaultNow(),
|
||||
})
|
||||
|
||||
// Lançamentos contábeis centralizados
|
||||
export const soeLancamentos = pgTable('soe_lancamentos', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
empresaId: integer('empresa_id').notNull(),
|
||||
data: date('data').notNull(),
|
||||
contaDebito: varchar('conta_debito', { length: 30 }).notNull(),
|
||||
contaCredito: varchar('conta_credito', { length: 30 }).notNull(),
|
||||
valor: numeric('valor', { precision: 15, scale: 2 }).notNull(),
|
||||
historico: text('historico').notNull(),
|
||||
origem: varchar('origem', { length: 100 }), // 'emissao_nfe', 'pagamento', etc.
|
||||
origemId: varchar('origem_id', { length: 100 }),
|
||||
period: varchar('period', { length: 7 }), // '2026-03'
|
||||
createdAt: timestamp('created_at').defaultNow(),
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. ROADMAP DE IMPLEMENTAÇÃO
|
||||
|
||||
### Fase 1 — Fundação (2–3 semanas)
|
||||
```
|
||||
[ ] 1. Criar server/soe/ com estrutura básica
|
||||
[ ] 2. Criar tabelas: soe_empresa_config, soe_regras, soe_eventos, soe_lancamentos
|
||||
[ ] 3. Implementar Plus Adapter (encapsula chamadas ao Laravel)
|
||||
[ ] 4. Implementar Frappe Adapter (encapsula chamadas ao ERPNext)
|
||||
[ ] 5. Endpoint GET /api/soe/status (health check de todos os motores)
|
||||
[ ] 6. Migrar /api/fisco/* para passar pelo SOE
|
||||
```
|
||||
|
||||
### Fase 2 — Central Fiscal (2–3 semanas)
|
||||
```
|
||||
[ ] 7. Implementar Rule Engine para fiscal
|
||||
[ ] 8. Criar regras fiscais base (NCM, CFOP, tributação por regime)
|
||||
[ ] 9. POST /api/soe/fiscal/emitir-nfe → Plus (invisível)
|
||||
[ ] 10. POST /api/soe/fiscal/cancelar-nfe → Plus (invisível)
|
||||
[ ] 11. GET /api/soe/fiscal/situacao → agrega Plus + Python 8002
|
||||
[ ] 12. Atualizar frontend /fisco para usar /api/soe/fiscal/*
|
||||
```
|
||||
|
||||
### Fase 3 — Central Contábil (2 semanas)
|
||||
```
|
||||
[ ] 13. Implementar auto-lancamentos para eventos fiscais
|
||||
[ ] 14. Integrar Motor Python 8003 via SOE
|
||||
[ ] 15. GET /api/soe/contabil/dre → resultado automático
|
||||
[ ] 16. GET /api/soe/contabil/balancete → atualizado em tempo real
|
||||
[ ] 17. Atualizar frontend /contabil para usar /api/soe/contabil/*
|
||||
```
|
||||
|
||||
### Fase 4 — Central Financeiro (2 semanas)
|
||||
```
|
||||
[ ] 18. Regras de boleto automático (via Asaas)
|
||||
[ ] 19. Alertas de vencimento (via Manus/WhatsApp)
|
||||
[ ] 20. Conciliação bancária semi-automática
|
||||
[ ] 21. Previsão financeira com IA (Motor Python 8003 + Manus)
|
||||
[ ] 22. Atualizar frontend /financeiro para usar /api/soe/financeiro/*
|
||||
```
|
||||
|
||||
### Fase 5 — Manus + SOE (1 semana)
|
||||
```
|
||||
[ ] 23. Registrar novas tools Manus apontando para SOE
|
||||
[ ] 24. Manus usa SOE para responder perguntas de negócio
|
||||
[ ] 25. Dashboard SOE: visão geral de todos os eventos
|
||||
```
|
||||
|
||||
### Fase 6 — Central ERP (3 semanas)
|
||||
```
|
||||
[ ] 26. Frappe Adapter completo (produtos, pedidos, estoque, compras)
|
||||
[ ] 27. Regras de negócio ERP no SOE
|
||||
[ ] 28. Sincronização bidirecional SOE ↔ ERPNext (via eventos)
|
||||
[ ] 29. Atualizar /erp para usar /api/soe/erp/*
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. RESULTADO FINAL — O QUE O USUÁRIO EXPERIMENTA
|
||||
|
||||
```
|
||||
Antes (fragmentado):
|
||||
Usuário → /plus → vê Laravel diretamente
|
||||
Usuário → /erp → vê ERPNext diretamente
|
||||
Regras fiscais só no Plus
|
||||
Contabilidade manual
|
||||
Financeiro sem automação
|
||||
|
||||
Depois (SOE Central):
|
||||
Usuário → /fisco → emite NF-e, Plus executa invisível
|
||||
Usuário → /contabil → vê DRE gerado automaticamente
|
||||
Usuário → /financeiro → vê fluxo, previsões, alertas automáticos
|
||||
Usuário → Manus → pergunta "qual meu resultado?" → resposta completa
|
||||
|
||||
Plus e ERPNext: INVISÍVEIS, executando em background
|
||||
Regras: CENTRALIZADAS no SOE, editáveis
|
||||
Lançamentos: AUTOMÁTICOS baseados em eventos (NF-e, pagamentos, etc.)
|
||||
Auditoria: TOTAL de todas as operações
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. PRINCÍPIO ORIENTADOR
|
||||
|
||||
> **"O usuário não sabe o que está por trás. Ele só sabe que funciona."**
|
||||
>
|
||||
> O Arcádia Suite é o **Sistema Operacional** da empresa.
|
||||
> O Plus e o ERPNext são **drivers de hardware** — poderosos, mas invisíveis.
|
||||
> O SOE Central é o **kernel** que os orquestra com regras e inteligência.
|
||||
|
||||
---
|
||||
|
||||
*PLANO_SOE_CENTRAL.md — Arcádia Suite v3.0*
|
||||
*Gerado em: 2026-03-16*
|
||||
|
|
@ -0,0 +1,666 @@
|
|||
# PLANO SOE CENTRAL v2 — Análise Real + Evolução
|
||||
## Baseado no MAPA_SOE_ARCADIA.md (Replit) + Auditoria do Código
|
||||
### Versão 2.0 — Março 2026
|
||||
|
||||
---
|
||||
|
||||
## 1. DIAGNÓSTICO — O QUE JÁ EXISTE (e o plano v1 não sabia)
|
||||
|
||||
O MAPA_SOE_ARCADIA.md do Replit revelou que **o SOE já é uma realidade arquitetural robusta**, não apenas um conceito. Antes de planejar qualquer coisa, é crítico entender o que já funciona.
|
||||
|
||||
### 1.1 O que já está construído e funcionando
|
||||
|
||||
| Componente | Status | Localização |
|
||||
|---|---|---|
|
||||
| `SOE.tsx` | ✅ 2.414 linhas — UI completa com 9 tabs | `client/src/pages/SOE.tsx` |
|
||||
| `SoeMotorContext.tsx` | ✅ Seletor de motor (plus/erpnext) | `client/src/contexts/` |
|
||||
| `ErpApiClient` (interface) | ✅ Interface unificada definida | `server/erp/index.ts` |
|
||||
| `ArcadiaPlusClient` | ✅ Adaptador HTTP → Plus:8080 | `server/erp/index.ts` |
|
||||
| `ArcadiaNextClient` | ✅ Adaptador HTTP → ERPNext API | `server/erp/index.ts` |
|
||||
| `registerSoeRoutes()` | ✅ 60+ endpoints em /api/soe/* | `server/erp/routes.ts` |
|
||||
| `server/plus/proxy.ts` | ✅ Proxy reverso para Plus | `server/plus/` |
|
||||
| `server/plus/sso.ts` | ✅ SSO automático Plus ↔ Suite | `server/plus/` |
|
||||
| `server/plus/launcher.ts` | ✅ Lança PHP artisan serve | `server/plus/` |
|
||||
| Módulo de Pessoas unificado | ✅ `persons` + `person_roles` | Schema PostgreSQL |
|
||||
| Módulo de Produtos | ✅ Com NCM, CST, CFOP, IMEI | Schema PostgreSQL |
|
||||
| Módulo Financeiro | ✅ Contas a P/R + transações | Schema PostgreSQL |
|
||||
| Motor Fisco (:8002) | ✅ NF-e/NFC-e via nfelib | `server/python/` |
|
||||
| Motor Contábil (:8003) | ✅ DRE, Balanço, SPED | `server/python/` |
|
||||
| Sistema de módulos por tenant | ✅ `tenants.features` (JSONB) | Schema PostgreSQL |
|
||||
| Middleware de gating | ✅ `requireModule()` | `server/` |
|
||||
|
||||
### 1.2 Bancos de Dados por Motor (detalhe crítico)
|
||||
|
||||
```
|
||||
Arcádia Suite → PostgreSQL 16 (dados do SOE, pessoas, produtos, vendas...)
|
||||
Arcádia Plus → MySQL 8.0 (dados do Laravel — vendas, estoque, NF-e...)
|
||||
ERPNext → MariaDB (dados do Frappe — CRM, HR, projetos...)
|
||||
Motor Fisco → sem banco (só processa, retorna resultado)
|
||||
Motor Contábil → sem banco (só processa, retorna resultado)
|
||||
```
|
||||
|
||||
**Implicação:** Não há sincronização automática entre esses bancos. Um pedido criado no SOE (PostgreSQL) não aparece no Plus (MySQL) automaticamente — e vice-versa.
|
||||
|
||||
### 1.3 O padrão motor/adaptador atual
|
||||
|
||||
```
|
||||
Request do usuário
|
||||
│
|
||||
▼
|
||||
/api/soe/sales-orders (POST)
|
||||
│
|
||||
▼
|
||||
Verifica motor ativo (localStorage: arcadia_soe_motor)
|
||||
│
|
||||
├── motor = "local" → INSERT direto no PostgreSQL
|
||||
├── motor = "plus" → POST → Plus:8080 → MySQL
|
||||
└── motor = "erpnext" → POST → ERPNext API → MariaDB
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. O PROBLEMA REAL — Onde o SOE atual para
|
||||
|
||||
O SOE atual é um **roteador inteligente**: recebe o request, decide para qual motor enviar, e despacha. Ele **não tem inteligência própria**.
|
||||
|
||||
### 2.1 O que falta (lacuna real)
|
||||
|
||||
```
|
||||
Situação atual:
|
||||
|
||||
SOE.tsx → /api/soe/* ────────────────────────────► Plus/ERPNext
|
||||
(sem regras, sem processamento)
|
||||
Regras ficam DENTRO do Plus
|
||||
Regras ficam DENTRO do ERPNext
|
||||
|
||||
Situação desejada:
|
||||
|
||||
SOE.tsx → /api/soe/* → [RULE ENGINE SOE] ──────► Plus/ERPNext
|
||||
│ (só executa)
|
||||
├── Regras Fiscais
|
||||
├── Regras Contábeis
|
||||
├── Regras Financeiras
|
||||
└── Regras de Negócio
|
||||
```
|
||||
|
||||
### 2.2 Problemas concretos hoje
|
||||
|
||||
| Problema | Impacto |
|
||||
|---|---|
|
||||
| Fiscal rules dentro do Plus | Se trocar de motor (Plus→ERPNext), perde todas as regras |
|
||||
| Nenhum lançamento contábil automático | DRE fica vazio, contabilidade manual |
|
||||
| Motor Contábil (:8003) existe mas nada alimenta ele | 0 lançamentos automáticos |
|
||||
| Plus tem UI própria (/plus) | Usuário pode bypassar o SOE e ir direto ao Plus |
|
||||
| Sem event bus entre motores | Uma NF-e emitida no Plus não dispara nada no SOE |
|
||||
| 25 conectores ERP definidos, zero chamadas reais | Integração existe no papel, não funciona |
|
||||
|
||||
### 2.3 O que o usuário vê hoje vs o que deveria ver
|
||||
|
||||
```
|
||||
Hoje:
|
||||
/soe → UI do SOE (tabs: dashboard, pessoas, produtos, vendas...)
|
||||
/plus → UI do Plus (Laravel) — direto, sem passar pelo SOE
|
||||
/erp → UI do ERP legado
|
||||
(usuário VÊ o Plus se quiser)
|
||||
|
||||
Desejado:
|
||||
/soe → UI do SOE (mesma, mas com inteligência)
|
||||
/plus → BLOQUEADO ou redirecionado para /soe
|
||||
/erp → BLOQUEADO ou redirecionado para /soe
|
||||
(Plus e ERPNext = invisíveis, executando em background)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. ARQUITETURA EVOLUÍDA — SOE com Rule Engine
|
||||
|
||||
### 3.1 O que muda (e o que fica igual)
|
||||
|
||||
**NÃO MUDA:**
|
||||
- `SOE.tsx` — a UI continua a mesma (só ganha funcionalidades)
|
||||
- `SoeMotorContext.tsx` — seleção de motor continua existindo
|
||||
- `ArcadiaPlusClient` / `ArcadiaNextClient` — adaptadores continuam
|
||||
- Todas as rotas `/api/soe/*` — continuam funcionando
|
||||
- Todos os bancos — PostgreSQL, MySQL, MariaDB — continuam separados
|
||||
|
||||
**MUDA / ADICIONA:**
|
||||
- Uma **camada de Rule Engine** entre as rotas e os adaptadores
|
||||
- **Event Bus SOE** que dispara lançamentos contábeis automaticamente
|
||||
- **Ocultação do Plus** — `/plus` passa a ser controlado pelo SOE
|
||||
- **Regras fiscais** saem do Plus e entram no SOE
|
||||
|
||||
### 3.2 Diagrama completo com Rule Engine
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ ARCÁDIA SUITE — CAMADA DE APRESENTAÇÃO │
|
||||
│ │
|
||||
│ SOE.tsx (2.414+ linhas) │
|
||||
│ Tabs: Dashboard | Pessoas | Produtos | Vendas | Compras │
|
||||
│ Financeiro | CRM | Sincronização | Config │
|
||||
│ │
|
||||
│ SoeMotorContext.tsx → motor = "plus" | "erpnext" | "local" │
|
||||
└───────────────────────────────┬─────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ /api/soe/* — CAMADA DE API (já existe) │
|
||||
│ registerSoeRoutes() em server/erp/routes.ts │
|
||||
│ requireAuth + requireModule middleware │
|
||||
└───────────────────────────────┬─────────────────────────────────────┘
|
||||
│
|
||||
▼ ◄── INSERIR AQUI (novo)
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ SOE RULE ENGINE (NOVO) │
|
||||
│ server/soe/rule-engine/ │
|
||||
│ │
|
||||
│ Antes de despachar para o motor, o Rule Engine: │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ 1. FISCAL RULES (server/soe/rule-engine/fiscal.ts) │ │
|
||||
│ │ • Resolve CFOP correto (intra/interestadual) │ │
|
||||
│ │ • Aplica tributação por regime (Simples/Presumido/Real) │ │
|
||||
│ │ • Valida NCM obrigatório │ │
|
||||
│ │ • Define CST/CSOSN automático │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ 2. BUSINESS RULES (server/soe/rule-engine/business.ts) │ │
|
||||
│ │ • Aprovação automática de pedidos < R$ X │ │
|
||||
│ │ • Reserva de estoque ao confirmar venda │ │
|
||||
│ │ • Trigger de NF-e ao faturar pedido │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ 3. ACCOUNTING RULES (server/soe/rule-engine/accounting.ts) │ │
|
||||
│ │ • Emitiu NF-e → lançamento automático D: Clientes │ │
|
||||
│ │ C: Receita │ │
|
||||
│ │ • Pagou conta → lançamento automático D: Fornecedor │ │
|
||||
│ │ C: Banco │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
└───────────────────────────────┬─────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ DESPACHADOR DE MOTOR (já existe, aprimorado) │
|
||||
│ createErpClient(connection) em server/erp/index.ts │
|
||||
│ │
|
||||
│ motor = "local" → INSERT PostgreSQL │
|
||||
│ motor = "plus" → ArcadiaPlusClient → Plus:8080 → MySQL │
|
||||
│ motor = "erpnext" → ArcadiaNextClient → ERPNext API → MariaDB │
|
||||
└───────────────────────────────┬─────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ SOE EVENT BUS (NOVO) │
|
||||
│ server/soe/event-bus.ts │
|
||||
│ │
|
||||
│ Depois que o motor executa, o Event Bus dispara: │
|
||||
│ │
|
||||
│ evento: "nfe_emitida" → Motor Contábil (lançamento auto) │
|
||||
│ evento: "venda_confirmada" → Reserva estoque + alerta WhatsApp │
|
||||
│ evento: "pagamento_feito" → Baixa C/P + lançamento contábil │
|
||||
│ evento: "recebimento" → Baixa C/R + lançamento contábil │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. O QUE CONSTRUIR — Detalhamento por Módulo
|
||||
|
||||
### 4.1 SOE Rule Engine (PRIORIDADE MÁXIMA)
|
||||
|
||||
**Localização:** `server/soe/rule-engine/`
|
||||
|
||||
Este é o coração da evolução. É um middleware que intercepta as chamadas às rotas SOE **antes** de despachar para o motor.
|
||||
|
||||
#### Como funciona na prática
|
||||
|
||||
```typescript
|
||||
// server/erp/routes.ts — modificação cirúrgica
|
||||
|
||||
// ANTES (atual):
|
||||
app.post("/api/soe/sales-orders", requireAuth, async (req, res) => {
|
||||
const client = getActiveClient(req) // pega Plus ou ERPNext
|
||||
const result = await client.createSalesOrder(req.body) // despacha direto
|
||||
res.json(result)
|
||||
})
|
||||
|
||||
// DEPOIS (com Rule Engine):
|
||||
app.post("/api/soe/sales-orders", requireAuth, async (req, res) => {
|
||||
// 1. Aplica regras ANTES de despachar
|
||||
const enriched = await ruleEngine.apply('pre_sales_order', req.body, req.user)
|
||||
|
||||
// 2. Despacha para o motor com dados enriquecidos
|
||||
const client = getActiveClient(req)
|
||||
const result = await client.createSalesOrder(enriched.payload)
|
||||
|
||||
// 3. Dispara eventos DEPOIS da execução
|
||||
await eventBus.emit('sales_order_created', { result, rules: enriched.appliedRules })
|
||||
|
||||
res.json(result)
|
||||
})
|
||||
```
|
||||
|
||||
#### Estrutura do Rule Engine
|
||||
|
||||
```typescript
|
||||
// server/soe/rule-engine/index.ts
|
||||
|
||||
interface SoeRule {
|
||||
id: string
|
||||
dominio: 'fiscal' | 'business' | 'accounting' | 'financial'
|
||||
trigger: string // ex: 'pre_sales_order', 'post_nfe_emitida'
|
||||
condicao: RuleCondition // quando esta regra se aplica
|
||||
acao: RuleAction // o que ela faz ao payload
|
||||
prioridade: number
|
||||
ativo: boolean
|
||||
empresaId?: number // null = regra global de todos os tenants
|
||||
}
|
||||
|
||||
class SoeRuleEngine {
|
||||
async apply(trigger: string, payload: any, context: RuleContext): Promise<EnrichedPayload> {
|
||||
// 1. Busca regras ativas para este trigger (do banco + defaults hardcoded)
|
||||
const rules = await this.getActiveRules(trigger, context.tenantId)
|
||||
|
||||
// 2. Filtra regras cuja condição se aplica ao payload
|
||||
const applicable = rules.filter(r => this.avaliarCondicao(r.condicao, payload, context))
|
||||
|
||||
// 3. Aplica cada regra em ordem de prioridade
|
||||
let enriched = { ...payload }
|
||||
const applied: string[] = []
|
||||
for (const rule of applicable.sort((a, b) => a.prioridade - b.prioridade)) {
|
||||
enriched = await this.aplicarAcao(rule.acao, enriched, context)
|
||||
applied.push(rule.id)
|
||||
}
|
||||
|
||||
return { payload: enriched, appliedRules: applied }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 Regras Fiscais no SOE
|
||||
|
||||
Estas regras **hoje vivem dentro do Plus** (Laravel controllers). A ideia é trazê-las para o SOE.
|
||||
|
||||
**Nota importante:** Não é mover o código PHP para TypeScript. É criar regras **declarativas** no SOE que enriquecem o payload **antes** de enviar ao Plus. O Plus ainda processa e emite — mas recebe o payload já correto.
|
||||
|
||||
```typescript
|
||||
// server/soe/rule-engine/fiscal-rules.ts
|
||||
|
||||
export const FISCAL_RULES_PADRAO: SoeRule[] = [
|
||||
|
||||
// CFOP — resolve automaticamente por operação
|
||||
{
|
||||
id: 'CFOP_VENDA_DENTRO_ESTADO',
|
||||
dominio: 'fiscal',
|
||||
trigger: 'pre_sales_order',
|
||||
condicao: { uf_emitente: '==', uf_destinatario: true, tipo_op: 'venda_produto' },
|
||||
acao: { set: { 'items[*].cfop': '5.102' } },
|
||||
prioridade: 10, ativo: true
|
||||
},
|
||||
{
|
||||
id: 'CFOP_VENDA_OUTRO_ESTADO',
|
||||
dominio: 'fiscal',
|
||||
trigger: 'pre_sales_order',
|
||||
condicao: { uf_emitente: '!=', uf_destinatario: true, tipo_op: 'venda_produto' },
|
||||
acao: { set: { 'items[*].cfop': '6.102' } },
|
||||
prioridade: 10, ativo: true
|
||||
},
|
||||
|
||||
// Tributação por Regime
|
||||
{
|
||||
id: 'CST_SIMPLES_NACIONAL',
|
||||
dominio: 'fiscal',
|
||||
trigger: 'pre_nfe_emissao',
|
||||
condicao: { regime_tributario: 'simples_nacional' },
|
||||
acao: {
|
||||
set: { 'items[*].cst_icms': '400', 'items[*].pis': null, 'items[*].cofins': null },
|
||||
// No Simples Nacional: CSOSN 400 = tributado, sem destaque PIS/COFINS
|
||||
},
|
||||
prioridade: 5, ativo: true
|
||||
},
|
||||
{
|
||||
id: 'CST_LUCRO_PRESUMIDO',
|
||||
dominio: 'fiscal',
|
||||
trigger: 'pre_nfe_emissao',
|
||||
condicao: { regime_tributario: 'lucro_presumido' },
|
||||
acao: { set: { 'items[*].cst_pis': '01', 'items[*].cst_cofins': '01' } },
|
||||
prioridade: 5, ativo: true
|
||||
},
|
||||
|
||||
// Validações obrigatórias
|
||||
{
|
||||
id: 'VALIDAR_NCM_NFE',
|
||||
dominio: 'fiscal',
|
||||
trigger: 'pre_nfe_emissao',
|
||||
condicao: { tipo_documento: ['nfe', 'nfce'] },
|
||||
acao: { validar: { campo: 'items[*].ncm', obrigatorio: true, formato: /^\d{8}$/ } },
|
||||
prioridade: 1, ativo: true
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
### 4.3 SOE Event Bus — Lançamentos Contábeis Automáticos
|
||||
|
||||
Este é o **maior salto de valor**. Hoje o Motor Contábil (:8003) existe mas nada o alimenta automaticamente.
|
||||
|
||||
```typescript
|
||||
// server/soe/event-bus.ts
|
||||
|
||||
type SoeEvent =
|
||||
| 'nfe_emitida'
|
||||
| 'nfe_cancelada'
|
||||
| 'venda_confirmada'
|
||||
| 'compra_recebida'
|
||||
| 'pagamento_realizado'
|
||||
| 'recebimento_realizado'
|
||||
| 'boleto_gerado'
|
||||
|
||||
interface SoeEventPayload {
|
||||
empresaId: number
|
||||
tenantId: number
|
||||
evento: SoeEvent
|
||||
dados: Record<string, any>
|
||||
motorOrigem: 'plus' | 'erpnext' | 'local'
|
||||
}
|
||||
|
||||
class SoeEventBus {
|
||||
async emit(evento: SoeEvent, payload: SoeEventPayload) {
|
||||
// Registra no banco (audit trail)
|
||||
await db.insert(soeEventos).values({ ...payload, createdAt: new Date() })
|
||||
|
||||
// Dispara handlers
|
||||
await Promise.allSettled(
|
||||
this.getHandlers(evento).map(h => h(payload))
|
||||
)
|
||||
}
|
||||
|
||||
// Handlers por evento
|
||||
private handlers: Record<SoeEvent, EventHandler[]> = {
|
||||
'nfe_emitida': [
|
||||
contabilHandler.lancamentoVenda, // D: Clientes / C: Receita Bruta
|
||||
contabilHandler.lancamentoImpostos, // D: Impostos / C: ICMS/PIS/COFINS a Recolher
|
||||
financialHandler.gerarRecebivel, // Cria C/R automaticamente
|
||||
],
|
||||
'pagamento_realizado': [
|
||||
contabilHandler.lancamentoPagamento, // D: Fornecedores / C: Banco
|
||||
financialHandler.baixarContaPagar, // Baixa C/P
|
||||
],
|
||||
'recebimento_realizado': [
|
||||
contabilHandler.lancamentoRecebimento, // D: Banco / C: Clientes
|
||||
financialHandler.baixarContaReceber, // Baixa C/R
|
||||
],
|
||||
'compra_recebida': [
|
||||
contabilHandler.lancamentoCompra, // D: Estoque / C: Fornecedores
|
||||
],
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Lançamentos automáticos para cada evento
|
||||
|
||||
```
|
||||
NF-e emitida (venda):
|
||||
D: 1.1.3.01 — Clientes (valor total da nota)
|
||||
C: 3.1.1.01 — Receita Bruta de Vendas
|
||||
|
||||
D: 3.1.2.01 — Deduções (ICMS)
|
||||
C: 2.1.2.01 — ICMS a Recolher
|
||||
|
||||
D: 3.1.2.02 — Deduções (PIS)
|
||||
C: 2.1.2.02 — PIS a Recolher
|
||||
|
||||
D: 3.1.2.03 — Deduções (COFINS)
|
||||
C: 2.1.2.03 — COFINS a Recolher
|
||||
|
||||
Pagamento de fornecedor:
|
||||
D: 2.1.1.{categoria} — Fornecedores
|
||||
C: 1.1.1.01 — Banco (conta configurada)
|
||||
|
||||
Recebimento de cliente:
|
||||
D: 1.1.1.01 — Banco
|
||||
C: 1.1.3.01 — Clientes
|
||||
|
||||
Compra recebida (NF-e entrada):
|
||||
D: 1.1.4.01 — Estoque / Mercadorias
|
||||
C: 2.1.1.01 — Fornecedores
|
||||
```
|
||||
|
||||
### 4.4 Ocultação do Plus e ERPNext
|
||||
|
||||
O Plus tem página própria em `/plus` (client/src/pages/Plus.tsx). O usuário pode acessá-la diretamente. Precisamos de 3 mudanças:
|
||||
|
||||
#### a) Redirecionar `/plus` para `/soe`
|
||||
|
||||
```typescript
|
||||
// client/src/App.tsx — modificação simples
|
||||
// Trocar: <Route path="/plus" component={Plus} />
|
||||
// Por: <Route path="/plus" render={() => <Redirect to="/soe" />} />
|
||||
```
|
||||
|
||||
**Mas manter o proxy reverso Plus ativo** — o Plus continua funcionando em background, só não fica visível como página separada.
|
||||
|
||||
#### b) Ocultar o seletor de motor do usuário final
|
||||
|
||||
```typescript
|
||||
// client/src/contexts/SoeMotorContext.tsx
|
||||
// O seletor fica SOMENTE na tab Config do SOE, e somente para admins
|
||||
|
||||
// A tab Config na SOE.tsx — adicionar role check:
|
||||
{user?.role === 'admin' && <TabsTrigger value="config">Config</TabsTrigger>}
|
||||
```
|
||||
|
||||
#### c) Na tab Config (admin), o label muda
|
||||
|
||||
```
|
||||
Antes: "Selecione o motor: Plus | ERPNext"
|
||||
Depois: "Motor de execução: Plus | ERPNext | Local"
|
||||
(invisível para usuário final, só admin de tenant vê)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. TABELAS A CRIAR (complemento ao schema existente)
|
||||
|
||||
As tabelas canônicas do SOE já existem. Precisamos adicionar apenas as tabelas do Rule Engine.
|
||||
|
||||
```typescript
|
||||
// shared/schema.ts — adicionar ao final
|
||||
|
||||
// Regras do SOE — configuráveis por tenant
|
||||
export const soeRegras = pgTable('soe_regras', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
tenantId: integer('tenant_id'), // null = regra global (todas as empresas)
|
||||
empresaId: integer('empresa_id'), // null = todas as empresas do tenant
|
||||
dominio: varchar('dominio', { length: 50 }).notNull(),
|
||||
trigger: varchar('trigger', { length: 100 }).notNull(),
|
||||
nome: varchar('nome', { length: 200 }).notNull(),
|
||||
condicao: jsonb('condicao').notNull(),
|
||||
acao: jsonb('acao').notNull(),
|
||||
prioridade: integer('prioridade').default(10),
|
||||
ativo: boolean('ativo').default(true),
|
||||
origemPadrao: boolean('origem_padrao').default(false), // true = regra default do sistema
|
||||
createdAt: timestamp('created_at').defaultNow(),
|
||||
updatedAt: timestamp('updated_at').defaultNow(),
|
||||
})
|
||||
|
||||
// Log de todos os eventos SOE (audit trail completo)
|
||||
export const soeEventos = pgTable('soe_eventos', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
tenantId: integer('tenant_id').notNull(),
|
||||
empresaId: integer('empresa_id'),
|
||||
evento: varchar('evento', { length: 100 }).notNull(),
|
||||
motorOrigem: varchar('motor_origem', { length: 50 }), // 'plus', 'erpnext', 'local'
|
||||
regraIds: jsonb('regra_ids'), // regras aplicadas
|
||||
payloadEntrada: jsonb('payload_entrada'),
|
||||
payloadSaida: jsonb('payload_saida'),
|
||||
status: varchar('status', { length: 50 }).notNull(),
|
||||
duracaoMs: integer('duracao_ms'),
|
||||
erro: text('erro'),
|
||||
createdAt: timestamp('created_at').defaultNow(),
|
||||
})
|
||||
|
||||
// Lançamentos contábeis automáticos do SOE
|
||||
export const soeLancamentos = pgTable('soe_lancamentos', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
tenantId: integer('tenant_id').notNull(),
|
||||
empresaId: integer('empresa_id').notNull(),
|
||||
data: date('data').notNull(),
|
||||
contaDebito: varchar('conta_debito', { length: 30 }).notNull(),
|
||||
contaCredito: varchar('conta_credito', { length: 30 }).notNull(),
|
||||
valor: numeric('valor', { precision: 15, scale: 2 }).notNull(),
|
||||
historico: text('historico').notNull(),
|
||||
origemEvento: varchar('origem_evento', { length: 100 }), // 'nfe_emitida', etc.
|
||||
origemEventoId: uuid('origem_evento_id'), // FK para soe_eventos
|
||||
periodo: varchar('periodo', { length: 7 }), // '2026-03'
|
||||
enviado8003: boolean('enviado_8003').default(false), // sincronizado com Motor Contábil
|
||||
createdAt: timestamp('created_at').defaultNow(),
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. ESTRUTURA DE PASTAS (Novo código a criar)
|
||||
|
||||
```
|
||||
server/
|
||||
└── soe/ ← NOVA PASTA
|
||||
├── rule-engine/
|
||||
│ ├── index.ts ← SoeRuleEngine class
|
||||
│ ├── fiscal-rules.ts ← Regras fiscais padrão
|
||||
│ ├── business-rules.ts ← Regras de negócio
|
||||
│ └── accounting-rules.ts ← Regras de lançamento contábil
|
||||
│
|
||||
├── event-bus.ts ← SoeEventBus class
|
||||
│
|
||||
├── handlers/
|
||||
│ ├── contabil-handler.ts ← Lançamentos automáticos
|
||||
│ └── financial-handler.ts ← Automação financeira
|
||||
│
|
||||
└── fiscal/
|
||||
└── cfop-resolver.ts ← Resolução de CFOP (lógica complexa)
|
||||
```
|
||||
|
||||
**Modificações em arquivos existentes:**
|
||||
```
|
||||
server/erp/routes.ts ← Inserir ruleEngine.apply() antes do dispatch
|
||||
client/src/App.tsx ← Redirecionar /plus para /soe
|
||||
client/src/pages/SOE.tsx ← Ocultar Config tab para não-admins
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. ROADMAP — Fases de Implementação
|
||||
|
||||
### Fase 1 — Rule Engine Básico ✅ CONCLUÍDA (2026-03-19) — João
|
||||
```
|
||||
[x] Criar server/soe/rule-engine/index.ts (SoeRuleEngine) — já existia | João | 2026-03-19
|
||||
[x] Criar tabelas soe_regras e soe_eventos (drizzle push) — já existiam no schema | João | 2026-03-19
|
||||
[x] Inserir ruleEngine.apply() em 3 rotas críticas:
|
||||
- POST /api/soe/sales-orders ✅ plugado | João | 2026-03-19
|
||||
- POST /api/soe/sales-orders/:id/generate-nfe ✅ plugado | João | 2026-03-19
|
||||
- POST /api/soe/purchase-orders ✅ plugado | João | 2026-03-19
|
||||
[x] Seed das regras fiscais padrão (CFOP, CST, validações NCM) — fiscal-rules.ts hardcoded | João | 2026-03-19
|
||||
[ ] Testar: criar pedido → verificar que payload está sendo enriquecido
|
||||
```
|
||||
|
||||
### Fase 2 — Event Bus + Contabilidade Automática ✅ CONCLUÍDA (2026-03-19) — João
|
||||
```
|
||||
[x] Criar server/soe/event-bus.ts — já existia | João | 2026-03-19
|
||||
[x] Criar tabela soe_lancamentos (drizzle push) — já existia no schema | João | 2026-03-19
|
||||
[x] Criar handlers: contabil-handler.ts + financial-handler.ts — já existiam | João | 2026-03-19
|
||||
[x] Conectar: POST generate-nfe → emit('nfe_emitida') → lançamentos ✅ plugado | João | 2026-03-19
|
||||
[ ] Conectar: pagamento/recebimento → emit() → lançamentos
|
||||
[ ] Criar endpoint: GET /api/soe/contabil/lancamentos?periodo=YYYY-MM
|
||||
[ ] DRE: agora tem dados reais (via soe_lancamentos → Motor :8003)
|
||||
```
|
||||
|
||||
### Fase 3 — Ocultar Plus/ERPNext ✅ CONCLUÍDA (2026-03-19) — João
|
||||
```
|
||||
[x] client/src/App.tsx: /plus → redirect para /soe ✅ | João | 2026-03-19
|
||||
[x] client/src/pages/SOE.tsx: Config tab só para admins ✅ | João | 2026-03-19
|
||||
[x] Manter proxy Plus funcionando (invisível) — proxy já existia, não alterado | João | 2026-03-19
|
||||
[ ] Testar: fluxo completo sem nenhum acesso direto ao Plus UI
|
||||
```
|
||||
|
||||
### Fase 4 — Regras Configuráveis pelo Usuário (1 semana)
|
||||
```
|
||||
[ ] UI na tab Config do SOE: "Regras de Negócio"
|
||||
[ ] CRUD de regras via /api/soe/rules
|
||||
[ ] Admin pode criar regras custom sem código
|
||||
[ ] Exemplo: "Pedidos acima de R$10.000 requerem aprovação manual"
|
||||
```
|
||||
|
||||
### Fase 5 — Financeiro Avançado (2 semanas)
|
||||
```
|
||||
[ ] Auto-boleto ao confirmar venda (Asaas API)
|
||||
[ ] Alertas de vencimento (Manus → WhatsApp)
|
||||
[ ] Conciliação bancária OFX semi-automática
|
||||
[ ] Previsão financeira (Motor BI :8004 + histórico soe_lancamentos)
|
||||
```
|
||||
|
||||
### Fase 6 — Contábil Avançado (2 semanas)
|
||||
```
|
||||
[ ] DRE automático mensal (100% alimentado pelo Event Bus)
|
||||
[ ] Balanço Patrimonial automático
|
||||
[ ] Fechamento contábil mensal
|
||||
[ ] SPED ECD/ECF alimentado por soe_lancamentos
|
||||
[ ] Exportação para escritório contábil (OFX, Excel, PDF)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. COMPARAÇÃO: PLANO v1 vs PLANO v2
|
||||
|
||||
| Aspecto | Plano v1 (antes do Replit .md) | Plano v2 (com conhecimento real) |
|
||||
|---|---|---|
|
||||
| Adaptadores Plus/ERPNext | "Precisam ser criados" | Já existem — não tocar |
|
||||
| SOE Routes | "Precisam ser criadas" | 60+ endpoints já existem |
|
||||
| SOE.tsx | "Precisa ser construída" | 2.414 linhas — já funciona |
|
||||
| Onde adicionar código | server/soe/ do zero | Middleware nas rotas existentes |
|
||||
| Esforço estimado | Alto (refatoração total) | Médio (adição cirúrgica) |
|
||||
| Risco de quebrar | Alto | Baixo (adição, não substituição) |
|
||||
| Banco Plus | Ignorado | MySQL — separado, nunca tocar direto |
|
||||
|
||||
---
|
||||
|
||||
## 9. PRINCÍPIO ORIENTADOR (REVISADO)
|
||||
|
||||
> O SOE já é o roteador certo. Agora vamos torná-lo o **árbitro de regras**.
|
||||
>
|
||||
> O padrão motor/adaptador está perfeito. O que falta é a **inteligência entre a rota e o adaptador**.
|
||||
>
|
||||
> Plus e ERPNext continuam existindo e funcionando — só que o usuário final não sabe.
|
||||
> Ele só vê a Central SOE. O restante é infraestrutura.
|
||||
|
||||
---
|
||||
|
||||
## 10. RESUMO EXECUTIVO — O que fazer agora
|
||||
|
||||
**1 ação que entrega valor imediato:**
|
||||
```
|
||||
Criar o SoeRuleEngine e plugar nas rotas de venda/NF-e.
|
||||
Resultado: Ao emitir uma NF-e, o CFOP correto é calculado
|
||||
automaticamente pelo SOE — sem o usuário saber que o Plus está
|
||||
sendo chamado por baixo.
|
||||
```
|
||||
|
||||
**1 ação que entrega valor a médio prazo:**
|
||||
```
|
||||
Criar o Event Bus com o handler de contabilidade.
|
||||
Resultado: Cada NF-e emitida gera lançamentos contábeis
|
||||
automaticamente. O DRE do mês é gerado sem entrada manual de dados.
|
||||
```
|
||||
|
||||
**1 ação que fecha o conceito:**
|
||||
```
|
||||
Redirecionar /plus → /soe.
|
||||
Resultado: O usuário não tem mais acesso ao Plus diretamente.
|
||||
Ele só vê o Arcádia. O Plus é infraestrutura invisível.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*PLANO_SOE_CENTRAL_v2.md — Arcádia Suite v3.0*
|
||||
*Gerado em: 2026-03-16 — Baseado na análise real do MAPA_SOE_ARCADIA.md (Replit)*
|
||||
|
|
@ -0,0 +1,469 @@
|
|||
# Requisitos Técnicos - Arcádia Suite
|
||||
## Documento de Arquitetura e Especificações Técnicas
|
||||
### Fevereiro 2026
|
||||
|
||||
---
|
||||
|
||||
## 1. VISÃO GERAL DO SISTEMA
|
||||
|
||||
**Nome:** Arcádia Suite - Sistema Operacional Empresarial (SOE)
|
||||
**Tipo:** Plataforma ERP/SaaS Multi-Tenant com Inteligência Artificial
|
||||
**Arquitetura:** Monolito Modular com Microserviços Python auxiliares
|
||||
**Licença:** Proprietário - Arcádia Business SIG
|
||||
|
||||
---
|
||||
|
||||
## 2. STACK TECNOLÓGICO
|
||||
|
||||
### 2.1 Linguagens de Programação
|
||||
|
||||
| Linguagem | Versão | Uso |
|
||||
|-----------|--------|-----|
|
||||
| TypeScript | 5.6.3 | Linguagem principal (Frontend + Backend) |
|
||||
| JavaScript (ES Modules) | ES2022 | Runtime Node.js |
|
||||
| Python | 3.11.14 | Microserviços de IA, BI, Fiscal, Contábil |
|
||||
| SQL | PostgreSQL 16 | Queries, migrations, procedures |
|
||||
| HTML5 / CSS3 | - | Marcação e estilos via JSX/Tailwind |
|
||||
|
||||
### 2.2 Runtime e Ambiente
|
||||
|
||||
| Componente | Versão | Descrição |
|
||||
|------------|--------|-----------|
|
||||
| Node.js | 20.20.0 | Runtime principal do servidor |
|
||||
| Python | 3.11.14 | Runtime dos microserviços auxiliares |
|
||||
| npm | 10.x | Gerenciador de pacotes Node.js |
|
||||
| pip | 24.x | Gerenciador de pacotes Python |
|
||||
|
||||
---
|
||||
|
||||
## 3. FRAMEWORKS E BIBLIOTECAS PRINCIPAIS
|
||||
|
||||
### 3.1 Backend (Servidor)
|
||||
|
||||
| Tecnologia | Versão | Função |
|
||||
|------------|--------|--------|
|
||||
| **Express.js** | 4.21.2 | Framework HTTP principal, API REST |
|
||||
| **Drizzle ORM** | 0.39.3 | ORM para PostgreSQL (type-safe) |
|
||||
| **Drizzle Kit** | 0.31.4 | Ferramenta de migrations do banco |
|
||||
| **Drizzle Zod** | 0.7.1 | Validação de schemas com Zod |
|
||||
| **Socket.IO** | 4.8.3 | Comunicação em tempo real (WebSocket) |
|
||||
| **Passport.js** | 0.7.0 | Autenticação (estratégia local) |
|
||||
| **Express Session** | 1.18.1 | Gerenciamento de sessões |
|
||||
| **Connect PG Simple** | 10.0.0 | Sessões persistidas no PostgreSQL |
|
||||
| **Zod** | 3.25.76 | Validação de dados e schemas |
|
||||
| **OpenAI SDK** | 6.15.0 | Integração com GPT-4o / GPT-4o-mini |
|
||||
| **Multer** | 2.0.2 | Upload de arquivos |
|
||||
| **Nodemailer** | 7.0.12 | Envio de emails (SMTP) |
|
||||
| **Octokit** | 22.0.1 | Integração com GitHub API |
|
||||
| **Baileys** | 7.0.0-rc.9 | WhatsApp Web API (multi-sessão) |
|
||||
| **node-fetch** | 3.3.2 | Requisições HTTP |
|
||||
| **pg** | 8.16.3 | Driver PostgreSQL nativo |
|
||||
| **ws** | 8.18.0 | WebSocket nativo |
|
||||
| **cheerio** | 1.1.2 | Web scraping / parsing HTML |
|
||||
| **pdf-parse** | 2.4.5 | Extração de texto de PDFs |
|
||||
| **xlsx** | 0.18.5 | Leitura/escrita de planilhas Excel |
|
||||
| **docx** | 9.5.1 | Geração de documentos Word |
|
||||
| **jspdf** | 4.0.0 | Geração de documentos PDF |
|
||||
| **papaparse** | 5.5.3 | Parsing de arquivos CSV |
|
||||
| **adm-zip** | 0.5.16 | Compactação/descompactação ZIP |
|
||||
| **qrcode** | 1.5.4 | Geração de QR Codes |
|
||||
| **imap / mailparser** | 0.8.19 / 3.9.1 | Leitura de emails (IMAP) |
|
||||
| **http-proxy-middleware** | 3.0.5 | Proxy reverso para microserviços |
|
||||
| **p-limit / p-retry** | 7.2.0 / 7.1.1 | Controle de concorrência e retry |
|
||||
| **esbuild** | 0.25.0 | Bundler do servidor para produção |
|
||||
| **tsx** | 4.20.5 | Execução de TypeScript em desenvolvimento |
|
||||
|
||||
### 3.2 Frontend (Cliente)
|
||||
|
||||
| Tecnologia | Versão | Função |
|
||||
|------------|--------|--------|
|
||||
| **React** | 19.2.0 | Biblioteca de UI principal |
|
||||
| **React DOM** | 19.2.0 | Renderização DOM |
|
||||
| **Vite** | 7.1.9 | Build tool e dev server |
|
||||
| **Tailwind CSS** | 4.1.14 | Framework de estilos utilitários |
|
||||
| **shadcn/ui (Radix UI)** | Múltiplas | 55 componentes de interface |
|
||||
| **TanStack React Query** | 5.60.5 | Gerenciamento de estado servidor |
|
||||
| **React Hook Form** | 7.66.0 | Gerenciamento de formulários |
|
||||
| **Wouter** | 3.3.5 | Roteamento SPA (leve) |
|
||||
| **Recharts** | 2.15.4 | Gráficos e visualização de dados |
|
||||
| **Framer Motion** | 12.23.24 | Animações e transições |
|
||||
| **Lucide React** | 0.545.0 | Biblioteca de ícones |
|
||||
| **Monaco Editor** | 4.7.0 | Editor de código (IDE integrada) |
|
||||
| **Xterm.js** | 5.3.0 | Terminal integrado |
|
||||
| **XYFlow (React Flow)** | 12.10.0 | Diagramas de fluxo e workflows |
|
||||
| **TipTap** | 3.15.3 | Editor de texto rico (WYSIWYG) |
|
||||
| **Embla Carousel** | 8.6.0 | Carrosséis de conteúdo |
|
||||
| **React Day Picker** | 9.11.1 | Seletor de datas |
|
||||
| **React Resizable Panels** | 2.1.9 | Painéis redimensionáveis |
|
||||
| **Socket.IO Client** | 4.8.3 | WebSocket no cliente |
|
||||
| **cmdk** | 1.1.1 | Command palette (Ctrl+K) |
|
||||
| **Sonner** | 2.0.7 | Notificações toast |
|
||||
| **html2canvas** | 1.4.1 | Captura de tela / screenshots |
|
||||
| **file-saver** | 2.0.5 | Download de arquivos |
|
||||
| **next-themes** | 0.4.6 | Tema claro/escuro |
|
||||
| **Vaul** | 1.1.2 | Drawer mobile |
|
||||
| **input-otp** | 1.4.2 | Input para códigos OTP |
|
||||
|
||||
### 3.3 Microserviços Python (FastAPI)
|
||||
|
||||
| Serviço | Porta | Função |
|
||||
|---------|-------|--------|
|
||||
| **Fiscal (Fisco)** | 8002 | NF-e/NFC-e via nfelib, comunicação SEFAZ |
|
||||
| **Contábil** | 8003 | Serviços contábeis auxiliares |
|
||||
| **People (RH)** | 8004 | Cálculos de folha de pagamento |
|
||||
| **BI Engine** | 8004 | Motor de Business Intelligence, SQL, Pandas |
|
||||
| **Automation Engine** | 8005 | Scheduler cron, event bus, workflows |
|
||||
|
||||
---
|
||||
|
||||
## 4. BANCO DE DADOS
|
||||
|
||||
### 4.1 Especificações
|
||||
|
||||
| Item | Detalhe |
|
||||
|------|---------|
|
||||
| **SGBD** | PostgreSQL 16 (Neon-backed na nuvem) |
|
||||
| **ORM** | Drizzle ORM (type-safe, schema-first) |
|
||||
| **Migrations** | Drizzle Kit (push strategy) |
|
||||
| **Schema** | Arquivo único: `shared/schema.ts` |
|
||||
| **Total de Tabelas** | 331 tabelas |
|
||||
| **Linhas de Schema** | 7.146 linhas |
|
||||
| **Sessões** | Persistidas em PostgreSQL (connect-pg-simple) |
|
||||
|
||||
### 4.2 Domínios de Dados (Principais)
|
||||
|
||||
| Domínio | Prefixo | Tabelas | Descrição |
|
||||
|---------|---------|---------|-----------|
|
||||
| Retail | `retail_*` | 24 | Lojas, vendedores, estoque, vendas, comissões |
|
||||
| CRM | `crm_*` | 23 | Leads, oportunidades, propostas, contratos |
|
||||
| Fiscal | `fiscal_*` | 11 | NCMs, CFOPs, notas fiscais, certificados |
|
||||
| Financeiro | `fin_*` | 7 | Contas bancárias, a pagar, a receber |
|
||||
| Contábil | `contabil_*` | 7 | Plano de contas, lançamentos, saldos |
|
||||
| People (RH) | `people_*` | 13 | Funcionários, folha, férias, ponto |
|
||||
| Qualidade | `quality_*` | 7 | Amostras, laudos, RNC, documentos |
|
||||
| Projetos | `pc_*` | 12 | Projetos, tarefas, membros, atividades |
|
||||
| XOS Governança | `xos_*` | 22 | Agents, pipelines, auditoria, políticas |
|
||||
| Comunicação | `comm_*` | 8 | Contatos, threads, mensagens unificadas |
|
||||
| WhatsApp | `whatsapp_*` | 5 | Sessões, mensagens, contatos, tickets |
|
||||
| Comunidades | `community_*` | 4 | Grupos, canais, membros, mensagens |
|
||||
| Valuation | `valuation_*` | 14 | Avaliação empresarial, cap table |
|
||||
| BI | `bi_*` | 4 | Dashboards, gráficos, datasets |
|
||||
| Automações | `automation_*` | 3 | Workflows, ações, logs |
|
||||
| Knowledge | `graph_*` | 2 | Grafo semântico (nós e arestas) |
|
||||
| Marketplace | `marketplace_*` | 3 | Módulos, assinaturas, uso |
|
||||
| DocTypes (FA) | `arc_*` / `doctype_*` | 9 | Framework de documentos customizados |
|
||||
| Multi-Tenant | `tenants` | 1 | Tenants com features JSONB |
|
||||
|
||||
---
|
||||
|
||||
## 5. ARQUITETURA DO SISTEMA
|
||||
|
||||
### 5.1 Diagrama de Camadas
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ CAMADA DE APRESENTAÇÃO │
|
||||
│ │
|
||||
│ React 19 + TypeScript + Tailwind CSS 4 + shadcn/ui │
|
||||
│ 55 componentes UI + 67 páginas │
|
||||
│ SPA com roteamento Wouter │
|
||||
│ 89.858 linhas de código │
|
||||
│ │
|
||||
│ Porta: 5000 (dev via Vite HMR) │
|
||||
└────────────────────────────┬────────────────────────────────────────┘
|
||||
│ HTTP/WebSocket
|
||||
┌────────────────────────────▼────────────────────────────────────────┐
|
||||
│ CAMADA DE ORQUESTRAÇÃO │
|
||||
│ │
|
||||
│ Express.js 4 + Socket.IO 4 + Passport.js │
|
||||
│ 45 arquivos de rotas │ ~1.386 endpoints REST │
|
||||
│ 153 arquivos TypeScript servidor │
|
||||
│ 58.393 linhas de código │
|
||||
│ │
|
||||
│ Porta: 5000 (API + WebSocket + Static) │
|
||||
└──────┬─────────┬──────────┬──────────┬─────────┬───────────────────┘
|
||||
│ │ │ │ │
|
||||
┌──────▼───┐ ┌───▼────┐ ┌──▼───┐ ┌────▼──┐ ┌───▼─────┐
|
||||
│ Fisco │ │Contábil│ │People│ │ BI │ │Automação│
|
||||
│ FastAPI │ │FastAPI │ │FastAPI│ │FastAPI│ │FastAPI │
|
||||
│ :8002 │ │ :8003 │ │:8004 │ │:8004 │ │ :8005 │
|
||||
└──────────┘ └────────┘ └──────┘ └───────┘ └─────────┘
|
||||
│ │ │ │ │
|
||||
┌──────▼─────────▼──────────▼──────────▼─────────▼───────────────────┐
|
||||
│ CAMADA DE DADOS │
|
||||
│ │
|
||||
│ PostgreSQL 16 (Neon) │ 331 tabelas │ Drizzle ORM │
|
||||
│ Schema: 7.146 linhas │ Multi-Tenant com JSONB features │
|
||||
│ Sessões persistidas em PostgreSQL │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 5.2 Padrões Arquiteturais
|
||||
|
||||
| Padrão | Implementação |
|
||||
|--------|--------------|
|
||||
| **Monolito Modular** | Servidor Node.js único com módulos isolados por domínio |
|
||||
| **Multi-Tenant** | Coluna `tenantId` em todas as tabelas, tenant scoping obrigatório |
|
||||
| **Schema-First** | Schema Drizzle como fonte de verdade para tipos e validação |
|
||||
| **API RESTful** | Endpoints organizados por domínio (`/api/{modulo}/*`) |
|
||||
| **Proxy Pattern** | Microserviços Python acessados via proxy reverso Express |
|
||||
| **Event-Driven** | Socket.IO para comunicação real-time (WhatsApp, chat, notificações) |
|
||||
| **Adapter Pattern** | Motors (Plus, ERPNext, Fisco) selecionados por adaptador |
|
||||
| **Singleton** | ManusIntelligence como cérebro central de IA único |
|
||||
| **Agent Loop** | Thought → Action → Observation para agentes autônomos |
|
||||
|
||||
---
|
||||
|
||||
## 6. INTEGRAÇÕES EXTERNAS
|
||||
|
||||
### 6.1 APIs e Serviços
|
||||
|
||||
| Integração | Protocolo | Uso |
|
||||
|------------|-----------|-----|
|
||||
| **OpenAI API** | REST (HTTPS) | GPT-4o para agentes IA, GPT-4o-mini para auto-replies |
|
||||
| **WhatsApp (Baileys)** | WebSocket | Multi-sessão WhatsApp, mensagens, tickets |
|
||||
| **GitHub API (Octokit)** | REST (HTTPS) | Commits automáticos, PRs, análise de código |
|
||||
| **ERPNext** | REST (HTTPS) | Clientes, produtos, ordens de venda, financeiro |
|
||||
| **Arcádia Plus (Laravel)** | REST (HTTPS) | ERP completo, NF-e/NFC-e, POS, fiscal |
|
||||
| **SEFAZ** | SOAP/XML | Emissão e consulta de notas fiscais eletrônicas |
|
||||
| **SMTP/IMAP** | Email | Envio (Nodemailer) e recebimento (IMAP) de emails |
|
||||
| **Portais Bancários** | Planejado | Conciliação bancária automatizada |
|
||||
|
||||
### 6.2 Protocolos de Interoperabilidade
|
||||
|
||||
| Protocolo | Endpoint | Descrição |
|
||||
|-----------|----------|-----------|
|
||||
| **MCP** (Model Context Protocol) | `/api/mcp/v1/` | Exposição de ferramentas para agentes externos |
|
||||
| **A2A** (Agent to Agent) | `/api/a2a/v1/` | Comunicação bidirecional entre agentes |
|
||||
| **REST API** | `/api/*` | API principal (~1.386 endpoints) |
|
||||
| **WebSocket** | Socket.IO | Real-time: chat, WhatsApp, notificações |
|
||||
|
||||
---
|
||||
|
||||
## 7. SEGURANÇA
|
||||
|
||||
| Aspecto | Implementação |
|
||||
|---------|--------------|
|
||||
| **Autenticação** | Passport.js (estratégia local) com sessões PostgreSQL |
|
||||
| **Autorização** | Multi-tenant: tenant scoping obrigatório (403 se ausente) |
|
||||
| **Sessões** | Express Session + connect-pg-simple (persistidas no banco) |
|
||||
| **Governança** | XOS Governance Layer com 5 políticas de segurança |
|
||||
| **Auditoria** | Audit trail imutável (`xos_audit_trail`) |
|
||||
| **Políticas** | Proteção de arquivos críticos, bloqueio de comandos destrutivos, aprovação humana para produção |
|
||||
| **Secrets** | Gerenciados via variáveis de ambiente (nunca expostos em código) |
|
||||
| **API Keys** | Header `X-API-Key` para protocolos MCP/A2A |
|
||||
|
||||
---
|
||||
|
||||
## 8. INFRAESTRUTURA E DEPLOY
|
||||
|
||||
### 8.1 Ambiente de Desenvolvimento
|
||||
|
||||
| Item | Detalhe |
|
||||
|------|---------|
|
||||
| **Plataforma** | Replit (NixOS) |
|
||||
| **Dev Server** | Vite 7 com HMR (Hot Module Replacement) |
|
||||
| **Backend Dev** | tsx (execução direta de TypeScript) |
|
||||
| **Porta** | 5000 (unificada: frontend + API + WebSocket) |
|
||||
|
||||
### 8.2 Ambiente de Produção (VPS)
|
||||
|
||||
| Item | Detalhe |
|
||||
|------|---------|
|
||||
| **Containerização** | Docker + Docker Compose |
|
||||
| **Base Image** | `node:20-alpine` (multi-stage build) |
|
||||
| **Build** | Vite (frontend) + esbuild (backend → `dist/index.cjs`) |
|
||||
| **Banco** | PostgreSQL 16 Alpine (container separado) |
|
||||
| **Volumes** | `pgdata` (banco) + `uploads` (arquivos) |
|
||||
| **Healthcheck** | HTTP GET `/` a cada 30s |
|
||||
| **Porta** | 5000 |
|
||||
|
||||
### 8.3 Variáveis de Ambiente
|
||||
|
||||
| Variável | Obrigatória | Descrição |
|
||||
|----------|-------------|-----------|
|
||||
| `DATABASE_URL` | Sim | String de conexão PostgreSQL |
|
||||
| `SESSION_SECRET` | Sim | Chave secreta para sessões |
|
||||
| `SSO_SECRET` | Sim | Chave para SSO multi-tenant |
|
||||
| `PGHOST/PGPORT/PGUSER/PGPASSWORD/PGDATABASE` | Sim | Credenciais PostgreSQL |
|
||||
| `OPENAI_API_KEY` | Não | API key OpenAI (funcionalidades IA) |
|
||||
| `ERPNEXT_URL/API_KEY/API_SECRET` | Não | Integração ERPNext |
|
||||
| `GITHUB_TOKEN` | Não | Integração GitHub |
|
||||
| `MANAGER_PASSWORD` | Não | Senha do gerente (Retail) |
|
||||
| `PLUS_URL/PLUS_API_TOKEN` | Não | Integração Arcádia Plus |
|
||||
| `FISCO_PYTHON_URL` | Não | URL do serviço fiscal Python |
|
||||
| `CONTABIL_PYTHON_URL` | Não | URL do serviço contábil Python |
|
||||
|
||||
---
|
||||
|
||||
## 9. MÉTRICAS DO PROJETO
|
||||
|
||||
### 9.1 Tamanho do Código
|
||||
|
||||
| Métrica | Valor |
|
||||
|---------|-------|
|
||||
| **Linhas de código (Frontend)** | 89.858 |
|
||||
| **Linhas de código (Backend)** | 58.393 |
|
||||
| **Linhas de Schema (Banco)** | 7.146 |
|
||||
| **Total estimado** | ~155.000+ linhas |
|
||||
| **Arquivos TypeScript (Server)** | 153 |
|
||||
| **Arquivos TSX (Client)** | 154 |
|
||||
| **Arquivos Python** | 6 |
|
||||
| **Total de arquivos de código** | ~313 |
|
||||
|
||||
### 9.2 Complexidade
|
||||
|
||||
| Métrica | Valor |
|
||||
|---------|-------|
|
||||
| **Tabelas no banco de dados** | 331 |
|
||||
| **Endpoints de API** | ~1.386 |
|
||||
| **Páginas/Telas** | 67 |
|
||||
| **Componentes UI (shadcn)** | 55 |
|
||||
| **Arquivos de rotas (server)** | 45 |
|
||||
| **Módulos do servidor** | 40+ domínios |
|
||||
| **Dependências (produção)** | 108 pacotes |
|
||||
| **Dependências (desenvolvimento)** | 22 pacotes |
|
||||
|
||||
---
|
||||
|
||||
## 10. REQUISITOS MÍNIMOS DO SERVIDOR (VPS)
|
||||
|
||||
### 10.1 Hardware Recomendado
|
||||
|
||||
| Recurso | Mínimo | Recomendado |
|
||||
|---------|--------|-------------|
|
||||
| **CPU** | 2 vCPU | 4 vCPU |
|
||||
| **RAM** | 4 GB | 8 GB |
|
||||
| **Disco** | 40 GB SSD | 80 GB SSD |
|
||||
| **Banda** | 100 Mbps | 1 Gbps |
|
||||
| **SO** | Ubuntu 22.04+ / Debian 12+ | Ubuntu 24.04 LTS |
|
||||
|
||||
### 10.2 Software Necessário
|
||||
|
||||
| Software | Versão Mínima |
|
||||
|----------|--------------|
|
||||
| Docker | 24.0+ |
|
||||
| Docker Compose | 2.20+ |
|
||||
| Git | 2.40+ |
|
||||
| (Opcional) Nginx | 1.24+ (proxy reverso com SSL) |
|
||||
| (Opcional) Certbot | 2.0+ (certificados Let's Encrypt) |
|
||||
|
||||
### 10.3 Configuração Nginx (Proxy Reverso com SSL)
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name seu-dominio.com.br;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name seu-dominio.com.br;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/seu-dominio.com.br/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/seu-dominio.com.br/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:5000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. ESTRUTURA DE DIRETÓRIOS
|
||||
|
||||
```
|
||||
arcadia-suite/
|
||||
├── client/ # Frontend React
|
||||
│ ├── src/
|
||||
│ │ ├── pages/ # 67 páginas da aplicação
|
||||
│ │ ├── components/
|
||||
│ │ │ ├── ui/ # 55 componentes shadcn/ui
|
||||
│ │ │ └── *.tsx # Componentes de negócio
|
||||
│ │ ├── hooks/ # Custom hooks React
|
||||
│ │ ├── contexts/ # Context providers
|
||||
│ │ ├── lib/ # Utilitários
|
||||
│ │ ├── modules/ # Módulos dinâmicos
|
||||
│ │ ├── App.tsx # Roteamento principal
|
||||
│ │ └── main.tsx # Entry point
|
||||
│ └── index.html # HTML base
|
||||
│
|
||||
├── server/ # Backend Node.js
|
||||
│ ├── admin/ # Administração
|
||||
│ ├── retail/ # Módulo Retail (PDV)
|
||||
│ ├── crm/ # CRM
|
||||
│ ├── financeiro/ # Financeiro
|
||||
│ ├── contabil/ # Contabilidade
|
||||
│ ├── fisco/ # Fiscal
|
||||
│ ├── people/ # RH / Pessoas
|
||||
│ ├── quality/ # Qualidade
|
||||
│ ├── erp/ # SOE (Sistema Operacional)
|
||||
│ ├── bi/ # Business Intelligence
|
||||
│ ├── whatsapp/ # WhatsApp multi-sessão
|
||||
│ ├── chat/ # Chat interno
|
||||
│ ├── manus/ # Agente IA central
|
||||
│ ├── xos/ # Governança e Dev Center
|
||||
│ ├── autonomous/ # Agentes autônomos (6 agentes)
|
||||
│ ├── governance/ # Políticas e auditoria
|
||||
│ ├── protocols/ # MCP e A2A
|
||||
│ ├── python/ # 6 microserviços FastAPI
|
||||
│ ├── modules/ # Módulos dinâmicos
|
||||
│ ├── routes.ts # Registro central de rotas
|
||||
│ ├── storage.ts # Interface de armazenamento
|
||||
│ └── index.ts # Entry point do servidor
|
||||
│
|
||||
├── shared/ # Código compartilhado
|
||||
│ ├── schema.ts # Schema Drizzle (331 tabelas)
|
||||
│ └── schemas/ # Schemas de módulos dinâmicos
|
||||
│
|
||||
├── db/ # Configuração do banco
|
||||
├── migrations/ # Arquivos de migração
|
||||
├── script/ # Scripts de build
|
||||
│
|
||||
├── Dockerfile # Build de produção
|
||||
├── docker-compose.yml # Orquestração de containers
|
||||
├── deploy.sh # Script de deploy VPS
|
||||
├── .env.example # Template de variáveis
|
||||
├── package.json # Dependências Node.js
|
||||
├── tsconfig.json # Configuração TypeScript
|
||||
├── vite.config.ts # Configuração Vite
|
||||
├── drizzle.config.ts # Configuração Drizzle
|
||||
└── postcss.config.js # Configuração PostCSS
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. FLUXO DE BUILD E DEPLOY
|
||||
|
||||
```
|
||||
Desenvolvimento (Replit)
|
||||
│
|
||||
├── npm run dev → tsx server/index.ts (TypeScript direto)
|
||||
│ Vite HMR para frontend
|
||||
│
|
||||
├── npm run build → script/build.ts
|
||||
│ ├── Vite build → client/dist/ (frontend estático)
|
||||
│ └── esbuild bundle → dist/index.cjs (servidor minificado)
|
||||
│
|
||||
└── npm run start → node dist/index.cjs (produção)
|
||||
|
||||
Deploy (VPS Docker)
|
||||
│
|
||||
├── docker compose build → Multi-stage Dockerfile
|
||||
│ ├── Stage 1 (builder) → npm install + npm run build
|
||||
│ └── Stage 2 (runner) → Copia dist/ + node_modules
|
||||
│
|
||||
├── docker compose up -d → Inicia PostgreSQL + App
|
||||
│
|
||||
└── npx drizzle-kit push → Aplica schema no banco
|
||||
```
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# Dependências Python - Arcádia Suite
|
||||
# Copie este arquivo para requirements.txt no servidor
|
||||
# Ou instale manualmente: pip install -r docs/python-requirements.txt
|
||||
|
||||
# FastAPI e servidor
|
||||
fastapi>=0.109.0
|
||||
uvicorn[standard]>=0.27.0
|
||||
python-multipart>=0.0.6
|
||||
|
||||
# NF-e / NFC-e (Fisco)
|
||||
nfelib>=1.0.0
|
||||
lxml>=4.9.0
|
||||
signxml>=3.0.0
|
||||
|
||||
# Banco de dados
|
||||
psycopg2-binary>=2.9.0
|
||||
sqlalchemy>=2.0.0
|
||||
|
||||
# Utilitários
|
||||
python-dotenv>=1.0.0
|
||||
pydantic>=2.0.0
|
||||
httpx>=0.26.0
|
||||
python-jose[cryptography]>=3.3.0
|
||||
passlib[bcrypt]>=1.7.4
|
||||
|
||||
# OpenAI (opcional - para IA)
|
||||
openai>=1.0.0
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
{
|
||||
"task_meta": {
|
||||
"task_id": "task_5ff59a7a780c",
|
||||
"attempt_id": 1,
|
||||
"retry_id": 0,
|
||||
"task_description": "",
|
||||
"task_file_name": null,
|
||||
"status": "pending",
|
||||
"start_time": null,
|
||||
"end_time": null,
|
||||
"final_boxed_answer": "",
|
||||
"judge_result": "",
|
||||
"verifier_name": "",
|
||||
"error": null,
|
||||
"ground_truth": null,
|
||||
"is_valid_box": null,
|
||||
"exceed_max_turn_summary": null,
|
||||
"used_exceed_max_turn_summaries": false,
|
||||
"previous_retry_ids": [],
|
||||
"updated_at": "2026-03-25T17:03:55.759Z"
|
||||
},
|
||||
"current_span": {
|
||||
"span_id": "sp_717d5ad094f5456c",
|
||||
"name": "IterativeAgentWithToolAndRollback(arcadia_statistician).run",
|
||||
"parent_span_id": null,
|
||||
"task_id": "root",
|
||||
"attempt_id": 0,
|
||||
"retry_id": 0,
|
||||
"start_ts": 1774458335.3193867,
|
||||
"end_ts": null,
|
||||
"status": "ok",
|
||||
"attrs": {
|
||||
"path": "IterativeAgentWithToolAndRollback(arcadia_statistician).run"
|
||||
},
|
||||
"error": null
|
||||
},
|
||||
"agent_states": {
|
||||
"arcadia_statistician": {
|
||||
"updated_at": "2026-03-25T17:05:35.320Z",
|
||||
"state": {
|
||||
"input_ctx": {
|
||||
"task_description": "quantos clientes temos\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"step_logs": [
|
||||
{
|
||||
"type": "log_debug",
|
||||
"msg": "Successfully patched OpenAI SDK models to allow extra fields",
|
||||
"ts": "2026-03-25T17:03:55.759Z",
|
||||
"seq": 1
|
||||
},
|
||||
{
|
||||
"type": "log_warning",
|
||||
"msg": "Failed to import package miroflow.io_processor: No module named 'mammoth'",
|
||||
"ts": "2026-03-25T17:05:35.197Z",
|
||||
"seq": 2
|
||||
},
|
||||
{
|
||||
"type": "log_info",
|
||||
"msg": "LLMClient (class=UnifiedOpenAIClient,provider=UnifiedOpenAIClient,model_name=deepseek-r1:14b) (cfg={'provider_class': 'UnifiedOpenAIClient', 'model_name': 'deepseek-r1:14b', 'api_key': 'ollama', 'base_url': 'http://localhost:11434/v1', 'max_tokens': 8192, 'temperature': 0.7, 'top_p': 1.0, 'min_p': 0.0, 'top_k': -1, 'reasoning_effort': None, 'repetition_penalty': 1.0, 'max_context_length': -1, 'async_client': True, 'disable_cache_control': True, 'keep_tool_result': -1, 'use_tool_calls': False, 'oai_tool_thinking': False}) initialized",
|
||||
"ts": "2026-03-25T17:05:35.316Z",
|
||||
"seq": 3
|
||||
},
|
||||
{
|
||||
"type": "log_info",
|
||||
"msg": "ToolManager initialized, loaded servers: []",
|
||||
"ts": "2026-03-25T17:05:35.317Z",
|
||||
"seq": 4
|
||||
},
|
||||
{
|
||||
"type": "log_info",
|
||||
"msg": "ToolManager initialized, loaded servers: []",
|
||||
"ts": "2026-03-25T17:05:35.318Z",
|
||||
"seq": 5
|
||||
},
|
||||
{
|
||||
"type": "log_info",
|
||||
"msg": "ToolManager initialized, loaded servers: []",
|
||||
"ts": "2026-03-25T17:05:35.318Z",
|
||||
"seq": 6
|
||||
},
|
||||
{
|
||||
"type": "span_start",
|
||||
"span_id": "sp_717d5ad094f5456c",
|
||||
"parent_span_id": null,
|
||||
"path": "IterativeAgentWithToolAndRollback(arcadia_statistician).run",
|
||||
"start_ts": 1774458335.3193867,
|
||||
"ts": "2026-03-25T17:05:35.319Z",
|
||||
"seq": 7
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
MIT License
|
||||
|
||||
Copyright © 2010 Sebastian Tschan, https://blueimp.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
22
plus/public/assets/vendor/datatables.net-fixedcolumns-bs5/License.txt
generated
vendored
Normal file
22
plus/public/assets/vendor/datatables.net-fixedcolumns-bs5/License.txt
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
22
plus/public/assets/vendor/datatables.net-fixedheader-bs5/License.txt
generated
vendored
Normal file
22
plus/public/assets/vendor/datatables.net-fixedheader-bs5/License.txt
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
22
plus/public/assets/vendor/datatables.net-responsive-bs5/License.txt
generated
vendored
Normal file
22
plus/public/assets/vendor/datatables.net-responsive-bs5/License.txt
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright SpryMedia Limited and other contributors
|
||||
http://datatables.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Adam Shaw
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
Authors ordered by first contribution
|
||||
A list of current team members is available at http://jqueryui.com/about
|
||||
|
||||
Paul Bakaus <paul.bakaus@gmail.com>
|
||||
Richard Worth <rdworth@gmail.com>
|
||||
Yehuda Katz <wycats@gmail.com>
|
||||
Sean Catchpole <sean@sunsean.com>
|
||||
John Resig <jeresig@gmail.com>
|
||||
Tane Piper <piper.tane@gmail.com>
|
||||
Dmitri Gaskin <dmitrig01@gmail.com>
|
||||
Klaus Hartl <klaus.hartl@gmail.com>
|
||||
Stefan Petre <stefan.petre@gmail.com>
|
||||
Gilles van den Hoven <gilles@webunity.nl>
|
||||
Micheil Bryan Smith <micheil@brandedcode.com>
|
||||
Jörn Zaefferer <joern.zaefferer@gmail.com>
|
||||
Marc Grabanski <m@marcgrabanski.com>
|
||||
Keith Wood <kbwood@iinet.com.au>
|
||||
Brandon Aaron <brandon.aaron@gmail.com>
|
||||
Scott González <scott.gonzalez@gmail.com>
|
||||
Eduardo Lundgren <eduardolundgren@gmail.com>
|
||||
Aaron Eisenberger <aaronchi@gmail.com>
|
||||
Joan Piedra <theneojp@gmail.com>
|
||||
Bruno Basto <b.basto@gmail.com>
|
||||
Remy Sharp <remy@leftlogic.com>
|
||||
Bohdan Ganicky <bohdan.ganicky@gmail.com>
|
||||
David Bolter <david.bolter@gmail.com>
|
||||
Chi Cheng <cloudream@gmail.com>
|
||||
Ca-Phun Ung <pazu2k@gmail.com>
|
||||
Ariel Flesler <aflesler@gmail.com>
|
||||
Maggie Wachs <maggie@filamentgroup.com>
|
||||
Scott Jehl <scott@scottjehl.com>
|
||||
Todd Parker <todd@filamentgroup.com>
|
||||
Andrew Powell <andrew@shellscape.org>
|
||||
Brant Burnett <btburnett3@gmail.com>
|
||||
Douglas Neiner <doug@dougneiner.com>
|
||||
Paul Irish <paul.irish@gmail.com>
|
||||
Ralph Whitbeck <ralph.whitbeck@gmail.com>
|
||||
Thibault Duplessis <thibault.duplessis@gmail.com>
|
||||
Dominique Vincent <dominique.vincent@toitl.com>
|
||||
Jack Hsu <jack.hsu@gmail.com>
|
||||
Adam Sontag <ajpiano@ajpiano.com>
|
||||
Carl Fürstenberg <carl@excito.com>
|
||||
Kevin Dalman <development@allpro.net>
|
||||
Alberto Fernández Capel <afcapel@gmail.com>
|
||||
Jacek Jędrzejewski (http://jacek.jedrzejewski.name)
|
||||
Ting Kuei <ting@kuei.com>
|
||||
Samuel Cormier-Iijima <sam@chide.it>
|
||||
Jon Palmer <jonspalmer@gmail.com>
|
||||
Ben Hollis <bhollis@amazon.com>
|
||||
Justin MacCarthy <Justin@Rubystars.biz>
|
||||
Eyal Kobrigo <kobrigo@hotmail.com>
|
||||
Tiago Freire <tiago.freire@gmail.com>
|
||||
Diego Tres <diegotres@gmail.com>
|
||||
Holger Rüprich <holger@rueprich.de>
|
||||
Ziling Zhao <zizhao@cisco.com>
|
||||
Mike Alsup <malsup@gmail.com>
|
||||
Robson Braga Araujo <robsonbraga@gmail.com>
|
||||
Pierre-Henri Ausseil <ph.ausseil@gmail.com>
|
||||
Christopher McCulloh <cmcculloh@gmail.com>
|
||||
Andrew Newcomb <ext.github@preceptsoftware.co.uk>
|
||||
Lim Chee Aun <cheeaun@gmail.com>
|
||||
Jorge Barreiro <yortx.barry@gmail.com>
|
||||
Daniel Steigerwald <daniel@steigerwald.cz>
|
||||
John Firebaugh <john_firebaugh@bigfix.com>
|
||||
John Enters <github@darkdark.net>
|
||||
Andrey Kapitcyn <ru.m157y@gmail.com>
|
||||
Dmitry Petrov <dpetroff@gmail.com>
|
||||
Eric Hynds <eric@hynds.net>
|
||||
Chairat Sunthornwiphat <pipo@sixhead.com>
|
||||
Josh Varner <josh.varner@gmail.com>
|
||||
Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||
Jay Merrifield <fracmak@gmail.com>
|
||||
J. Ryan Stinnett <jryans@gmail.com>
|
||||
Peter Heiberg <peter@heiberg.se>
|
||||
Alex Dovenmuehle <adovenmuehle@gmail.com>
|
||||
Jamie Gegerson <git@jamiegegerson.com>
|
||||
Raymond Schwartz <skeetergraphics@gmail.com>
|
||||
Phillip Barnes <philbar@gmail.com>
|
||||
Kyle Wilkinson <kai@wikyd.org>
|
||||
Khaled AlHourani <me@khaledalhourani.com>
|
||||
Marian Rudzynski <mr@impaled.org>
|
||||
Jean-Francois Remy <jeff@melix.org>
|
||||
Doug Blood
|
||||
Filippo Cavallarin <filippo.cavallarin@codseq.it>
|
||||
Heiko Henning <heiko@thehennings.ch>
|
||||
Aliaksandr Rahalevich <saksmlz@gmail.com>
|
||||
Mario Visic <mario@mariovisic.com>
|
||||
Xavi Ramirez <xavi.rmz@gmail.com>
|
||||
Max Schnur <max.schnur@gmail.com>
|
||||
Saji Nediyanchath <saji89@gmail.com>
|
||||
Corey Frang <gnarf37@gmail.com>
|
||||
Aaron Peterson <aaronp123@yahoo.com>
|
||||
Ivan Peters <ivan@ivanpeters.com>
|
||||
Mohamed Cherif Bouchelaghem <cherifbouchelaghem@yahoo.fr>
|
||||
Marcos Sousa <falecomigo@marcossousa.com>
|
||||
Michael DellaNoce <mdellanoce@mailtrust.com>
|
||||
George Marshall <echosx@gmail.com>
|
||||
Tobias Brunner <tobias@strongswan.org>
|
||||
Martin Solli <msolli@gmail.com>
|
||||
David Petersen <public@petersendidit.com>
|
||||
Dan Heberden <danheberden@gmail.com>
|
||||
William Kevin Manire <williamkmanire@gmail.com>
|
||||
Gilmore Davidson <gilmoreorless@gmail.com>
|
||||
Michael Wu <michaelmwu@gmail.com>
|
||||
Adam Parod <mystic414@gmail.com>
|
||||
Guillaume Gautreau <guillaume+github@ghusse.com>
|
||||
Marcel Toele <EleotleCram@gmail.com>
|
||||
Dan Streetman <ddstreet@ieee.org>
|
||||
Matt Hoskins <matt@nipltd.com>
|
||||
Giovanni Giacobbi <giovanni@giacobbi.net>
|
||||
Kyle Florence <kyle.florence@gmail.com>
|
||||
Pavol Hluchý <lopo@losys.sk>
|
||||
Hans Hillen <hans.hillen@gmail.com>
|
||||
Mark Johnson <virgofx@live.com>
|
||||
Trey Hunner <treyhunner@gmail.com>
|
||||
Shane Whittet <whittet@gmail.com>
|
||||
Edward A Faulkner <ef@alum.mit.edu>
|
||||
Adam Baratz <adam@adambaratz.com>
|
||||
Kato Kazuyoshi <kato.kazuyoshi@gmail.com>
|
||||
Eike Send <eike.send@gmail.com>
|
||||
Kris Borchers <kris.borchers@gmail.com>
|
||||
Eddie Monge <eddie@eddiemonge.com>
|
||||
Israel Tsadok <itsadok@gmail.com>
|
||||
Carson McDonald <carson@ioncannon.net>
|
||||
Jason Davies <jason@jasondavies.com>
|
||||
Garrison Locke <gplocke@gmail.com>
|
||||
David Murdoch <david@davidmurdoch.com>
|
||||
Benjamin Scott Boyle <benjamins.boyle@gmail.com>
|
||||
Jesse Baird <jebaird@gmail.com>
|
||||
Jonathan Vingiano <jvingiano@gmail.com>
|
||||
Dylan Just <dev@ephox.com>
|
||||
Hiroshi Tomita <tomykaira@gmail.com>
|
||||
Glenn Goodrich <glenn.goodrich@gmail.com>
|
||||
Tarafder Ashek-E-Elahi <mail.ashek@gmail.com>
|
||||
Ryan Neufeld <ryan@neufeldmail.com>
|
||||
Marc Neuwirth <marc.neuwirth@gmail.com>
|
||||
Philip Graham <philip.robert.graham@gmail.com>
|
||||
Benjamin Sterling <benjamin.sterling@kenzomedia.com>
|
||||
Wesley Walser <waw325@gmail.com>
|
||||
Kouhei Sutou <kou@clear-code.com>
|
||||
Karl Kirch <karlkrch@gmail.com>
|
||||
Chris Kelly <ckdake@ckdake.com>
|
||||
Jay Oster <jay@loyalize.com>
|
||||
Alexander Polomoshnov <alex.polomoshnov@gmail.com>
|
||||
David Leal <dgleal@gmail.com>
|
||||
Igor Milla <igor.fsp.milla@gmail.com>
|
||||
Dave Methvin <dave.methvin@gmail.com>
|
||||
Florian Gutmann <f.gutmann@chronimo.com>
|
||||
Marwan Al Jubeh <marwan.aljubeh@gmail.com>
|
||||
Milan Broum <midlis@googlemail.com>
|
||||
Sebastian Sauer <info@dynpages.de>
|
||||
Gaëtan Muller <m.gaetan89@gmail.com>
|
||||
William Griffiths <william@ycymro.com>
|
||||
Stojce Slavkovski <stojce@gmail.com>
|
||||
David Soms <david.soms@gmail.com>
|
||||
David De Sloovere <david.desloovere@outlook.com>
|
||||
Michael P. Jung <michael.jung@terreon.de>
|
||||
Shannon Pekary <spekary@gmail.com>
|
||||
Matthew Edward Hutton <meh@corefiling.co.uk>
|
||||
James Khoury <james@jameskhoury.com>
|
||||
Rob Loach <robloach@gmail.com>
|
||||
Alberto Monteiro <betimbrasil@gmail.com>
|
||||
Alex Rhea <alex.rhea@gmail.com>
|
||||
Krzysztof Rosiński <rozwell69@gmail.com>
|
||||
Ryan Olton <oltonr@gmail.com>
|
||||
Genie <386@mail.com>
|
||||
Rick Waldron <waldron.rick@gmail.com>
|
||||
Ian Simpson <spoonlikesham@gmail.com>
|
||||
Lev Kitsis <spam4lev@gmail.com>
|
||||
TJ VanToll <tj.vantoll@gmail.com>
|
||||
Justin Domnitz <jdomnitz@gmail.com>
|
||||
Douglas Cerna <douglascerna@yahoo.com>
|
||||
Bert ter Heide <bertjh@hotmail.com>
|
||||
Jasvir Nagra <jasvir@gmail.com>
|
||||
Yuriy Khabarov <13real008@gmail.com>
|
||||
Harri Kilpiö <harri.kilpio@gmail.com>
|
||||
Lado Lomidze <lado.lomidze@gmail.com>
|
||||
Amir E. Aharoni <amir.aharoni@mail.huji.ac.il>
|
||||
Simon Sattes <simon.sattes@gmail.com>
|
||||
Jo Liss <joliss42@gmail.com>
|
||||
Guntupalli Karunakar <karunakarg@yahoo.com>
|
||||
Shahyar Ghobadpour <shahyar@gmail.com>
|
||||
Lukasz Lipinski <uzza17@gmail.com>
|
||||
Timo Tijhof <krinklemail@gmail.com>
|
||||
Jason Moon <jmoon@socialcast.com>
|
||||
Martin Frost <martinf55@hotmail.com>
|
||||
Eneko Illarramendi <eneko@illarra.com>
|
||||
EungJun Yi <semtlenori@gmail.com>
|
||||
Courtland Allen <courtlandallen@gmail.com>
|
||||
Viktar Varvanovich <non4eg@gmail.com>
|
||||
Danny Trunk <dtrunk90@gmail.com>
|
||||
Pavel Stetina <pavel.stetina@nangu.tv>
|
||||
Michael Stay <metaweta@gmail.com>
|
||||
Steven Roussey <sroussey@gmail.com>
|
||||
Michael Hollis <hollis21@gmail.com>
|
||||
Lee Rowlands <lee.rowlands@previousnext.com.au>
|
||||
Timmy Willison <timmywillisn@gmail.com>
|
||||
Karl Swedberg <kswedberg@gmail.com>
|
||||
Baoju Yuan <the_guy_1987@hotmail.com>
|
||||
Maciej Mroziński <maciej.k.mrozinski@gmail.com>
|
||||
Luis Dalmolin <luis.nh@gmail.com>
|
||||
Mark Aaron Shirley <maspwr@gmail.com>
|
||||
Martin Hoch <martin@fidion.de>
|
||||
Jiayi Yang <tr870829@gmail.com>
|
||||
Philipp Benjamin Köppchen <xgxtpbk@gws.ms>
|
||||
Sindre Sorhus <sindresorhus@gmail.com>
|
||||
Bernhard Sirlinger <bernhard.sirlinger@tele2.de>
|
||||
Jared A. Scheel <jared@jaredscheel.com>
|
||||
Rafael Xavier de Souza <rxaviers@gmail.com>
|
||||
John Chen <zhang.z.chen@intel.com>
|
||||
Dale Kocian <dale.kocian@gmail.com>
|
||||
Mike Sherov <mike.sherov@gmail.com>
|
||||
Andrew Couch <andy@couchand.com>
|
||||
Marc-Andre Lafortune <github@marc-andre.ca>
|
||||
Nate Eagle <nate.eagle@teamaol.com>
|
||||
David Souther <davidsouther@gmail.com>
|
||||
Mathias Stenbom <mathias@stenbom.com>
|
||||
Sergey Kartashov <ebishkek@yandex.ru>
|
||||
Avinash R <nashpapa@gmail.com>
|
||||
Ethan Romba <ethanromba@gmail.com>
|
||||
Cory Gackenheimer <cory.gack@gmail.com>
|
||||
Juan Pablo Kaniefsky <jpkaniefsky@gmail.com>
|
||||
Roman Salnikov <bardt.dz@gmail.com>
|
||||
Anika Henke <anika@selfthinker.org>
|
||||
Samuel Bovée <samycookie2000@yahoo.fr>
|
||||
Fabrício Matté <ult_combo@hotmail.com>
|
||||
Viktor Kojouharov <vkojouharov@gmail.com>
|
||||
Pawel Maruszczyk (http://hrabstwo.net)
|
||||
Pavel Selitskas <p.selitskas@gmail.com>
|
||||
Bjørn Johansen <post@bjornjohansen.no>
|
||||
Matthieu Penant <thieum22@hotmail.com>
|
||||
Dominic Barnes <dominic@dbarnes.info>
|
||||
David Sullivan <david.sullivan@gmail.com>
|
||||
Thomas Jaggi <thomas@responsive.ch>
|
||||
Vahid Sohrabloo <vahid4134@gmail.com>
|
||||
Travis Carden <travis.carden@gmail.com>
|
||||
Bruno M. Custódio <bruno@brunomcustodio.com>
|
||||
Nathanael Silverman <nathanael.silverman@gmail.com>
|
||||
Christian Wenz <christian@wenz.org>
|
||||
Steve Urmston <steve@urm.st>
|
||||
Zaven Muradyan <megalivoithos@gmail.com>
|
||||
Woody Gilk <shadowhand@deviantart.com>
|
||||
Zbigniew Motyka <zbigniew.motyka@gmail.com>
|
||||
Suhail Alkowaileet <xsoh.k7@gmail.com>
|
||||
Toshi MARUYAMA <marutosijp2@yahoo.co.jp>
|
||||
David Hansen <hansede@gmail.com>
|
||||
Brian Grinstead <briangrinstead@gmail.com>
|
||||
Christian Klammer <christian314159@gmail.com>
|
||||
Steven Luscher <jquerycla@steveluscher.com>
|
||||
Gan Eng Chin <engchin.gan@gmail.com>
|
||||
Gabriel Schulhof <gabriel.schulhof@intel.com>
|
||||
Alexander Schmitz <arschmitz@gmail.com>
|
||||
Vilhjálmur Skúlason <vis@dmm.is>
|
||||
Siebrand Mazeland <s.mazeland@xs4all.nl>
|
||||
Mohsen Ekhtiari <mohsenekhtiari@yahoo.com>
|
||||
Pere Orga <gotrunks@gmail.com>
|
||||
Jasper de Groot <mail@ugomobi.com>
|
||||
Stephane Deschamps <stephane.deschamps@gmail.com>
|
||||
Jyoti Deka <dekajp@gmail.com>
|
||||
Andrei Picus <office.nightcrawler@gmail.com>
|
||||
Ondrej Novy <novy@ondrej.org>
|
||||
Jacob McCutcheon <jacob.mccutcheon@gmail.com>
|
||||
Monika Piotrowicz <monika.piotrowicz@gmail.com>
|
||||
Imants Horsts <imants.horsts@inbox.lv>
|
||||
Eric Dahl <eric.c.dahl@gmail.com>
|
||||
Dave Stein <dave@behance.com>
|
||||
Dylan Barrell <dylan@barrell.com>
|
||||
Daniel DeGroff <djdegroff@gmail.com>
|
||||
Michael Wiencek <mwtuea@gmail.com>
|
||||
Thomas Meyer <meyertee@gmail.com>
|
||||
Ruslan Yakhyaev <ruslan@ruslan.io>
|
||||
Brian J. Dowling <bjd-dev@simplicity.net>
|
||||
Ben Higgins <ben@extrahop.com>
|
||||
Yermo Lamers <yml@yml.com>
|
||||
Patrick Stapleton <github@gdi2290.com>
|
||||
Trisha Crowley <trisha.crowley@gmail.com>
|
||||
Usman Akeju <akeju00+github@gmail.com>
|
||||
Rodrigo Menezes <rod333@gmail.com>
|
||||
Jacques Perrault <jacques_perrault@us.ibm.com>
|
||||
Frederik Elvhage <frederik.elvhage@googlemail.com>
|
||||
Will Holley <willholley@gmail.com>
|
||||
Uri Gilad <antishok@gmail.com>
|
||||
Richard Gibson <richard.gibson@gmail.com>
|
||||
Simen Bekkhus <sbekkhus91@gmail.com>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
Copyright 2007, 2014 jQuery Foundation and other contributors,
|
||||
https://jquery.org/
|
||||
|
||||
This software consists of voluntary contributions made by many
|
||||
individuals. For exact contribution history, see the revision history
|
||||
available at https://github.com/jquery/jquery-ui
|
||||
|
||||
The following license applies to all parts of this software except as
|
||||
documented below:
|
||||
|
||||
====
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
====
|
||||
|
||||
Copyright and related rights for sample code are waived via CC0. Sample
|
||||
code is defined as all source code contained within the demos directory.
|
||||
|
||||
CC0: http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
====
|
||||
|
||||
All files located in the node_modules and external directories are
|
||||
externally maintained libraries used by this software which have their
|
||||
own licenses; we recommend you read them, as their terms may differ from
|
||||
the terms above.
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
|
||||
af.js
|
||||
----------------
|
||||
af.js
|
||||
|
||||
ar.js
|
||||
----------------
|
||||
ar.js
|
||||
|
||||
az.js
|
||||
----------------
|
||||
az.js
|
||||
|
||||
bg.js
|
||||
----------------
|
||||
bg.js
|
||||
|
||||
bn.js
|
||||
----------------
|
||||
bn.js
|
||||
|
||||
bs.js
|
||||
----------------
|
||||
bs.js
|
||||
|
||||
ca.js
|
||||
----------------
|
||||
ca.js
|
||||
|
||||
cs.js
|
||||
----------------
|
||||
cs.js
|
||||
|
||||
da.js
|
||||
----------------
|
||||
da.js
|
||||
|
||||
de.js
|
||||
----------------
|
||||
de.js
|
||||
|
||||
dsb.js
|
||||
----------------
|
||||
dsb.js
|
||||
|
||||
el.js
|
||||
----------------
|
||||
el.js
|
||||
|
||||
en.js
|
||||
----------------
|
||||
en.js
|
||||
|
||||
eo.js
|
||||
----------------
|
||||
eo.js
|
||||
|
||||
es.js
|
||||
----------------
|
||||
es.js
|
||||
|
||||
et.js
|
||||
----------------
|
||||
et.js
|
||||
|
||||
eu.js
|
||||
----------------
|
||||
eu.js
|
||||
|
||||
fa.js
|
||||
----------------
|
||||
fa.js
|
||||
|
||||
fi.js
|
||||
----------------
|
||||
fi.js
|
||||
|
||||
fr.js
|
||||
----------------
|
||||
fr.js
|
||||
|
||||
gl.js
|
||||
----------------
|
||||
gl.js
|
||||
|
||||
he.js
|
||||
----------------
|
||||
he.js
|
||||
|
||||
hi.js
|
||||
----------------
|
||||
hi.js
|
||||
|
||||
hr.js
|
||||
----------------
|
||||
hr.js
|
||||
|
||||
hsb.js
|
||||
----------------
|
||||
hsb.js
|
||||
|
||||
hu.js
|
||||
----------------
|
||||
hu.js
|
||||
|
||||
hy.js
|
||||
----------------
|
||||
hy.js
|
||||
|
||||
id.js
|
||||
----------------
|
||||
id.js
|
||||
|
||||
is.js
|
||||
----------------
|
||||
is.js
|
||||
|
||||
it.js
|
||||
----------------
|
||||
it.js
|
||||
|
||||
ja.js
|
||||
----------------
|
||||
ja.js
|
||||
|
||||
ka.js
|
||||
----------------
|
||||
ka.js
|
||||
|
||||
km.js
|
||||
----------------
|
||||
km.js
|
||||
|
||||
ko.js
|
||||
----------------
|
||||
ko.js
|
||||
|
||||
lt.js
|
||||
----------------
|
||||
lt.js
|
||||
|
||||
lv.js
|
||||
----------------
|
||||
lv.js
|
||||
|
||||
mk.js
|
||||
----------------
|
||||
mk.js
|
||||
|
||||
ms.js
|
||||
----------------
|
||||
ms.js
|
||||
|
||||
nb.js
|
||||
----------------
|
||||
nb.js
|
||||
|
||||
ne.js
|
||||
----------------
|
||||
ne.js
|
||||
|
||||
nl.js
|
||||
----------------
|
||||
nl.js
|
||||
|
||||
pa.js
|
||||
----------------
|
||||
pa.js
|
||||
|
||||
pl.js
|
||||
----------------
|
||||
pl.js
|
||||
|
||||
ps.js
|
||||
----------------
|
||||
ps.js
|
||||
|
||||
pt-BR.js
|
||||
----------------
|
||||
pt-BR.js
|
||||
|
||||
pt.js
|
||||
----------------
|
||||
pt.js
|
||||
|
||||
ro.js
|
||||
----------------
|
||||
ro.js
|
||||
|
||||
ru.js
|
||||
----------------
|
||||
ru.js
|
||||
|
||||
sk.js
|
||||
----------------
|
||||
sk.js
|
||||
|
||||
sl.js
|
||||
----------------
|
||||
sl.js
|
||||
|
||||
sq.js
|
||||
----------------
|
||||
sq.js
|
||||
|
||||
sr-Cyrl.js
|
||||
----------------
|
||||
sr-Cyrl.js
|
||||
|
||||
sr.js
|
||||
----------------
|
||||
sr.js
|
||||
|
||||
sv.js
|
||||
----------------
|
||||
sv.js
|
||||
|
||||
te.js
|
||||
----------------
|
||||
te.js
|
||||
|
||||
th.js
|
||||
----------------
|
||||
th.js
|
||||
|
||||
tk.js
|
||||
----------------
|
||||
tk.js
|
||||
|
||||
tr.js
|
||||
----------------
|
||||
tr.js
|
||||
|
||||
uk.js
|
||||
----------------
|
||||
uk.js
|
||||
|
||||
vi.js
|
||||
----------------
|
||||
vi.js
|
||||
|
||||
zh-CN.js
|
||||
----------------
|
||||
zh-CN.js
|
||||
|
||||
zh-TW.js
|
||||
----------------
|
||||
zh-TW.js
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 - Vincent Gabriel & Jason Gill
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue