199 lines
7.4 KiB
Python
199 lines
7.4 KiB
Python
"""Integration configuration and action routes.
|
|
|
|
Moved from app.admin_dashboard in v4.7.2. The handlers still reuse
|
|
legacy helpers to keep this refactor behavior-preserving.
|
|
"""
|
|
from fastapi import APIRouter
|
|
import app.admin_dashboard as legacy
|
|
from app.admin_dashboard import * # noqa: F401,F403
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/integrations", response_class=HTMLResponse)
|
|
@router.get("/integracoes", response_class=HTMLResponse)
|
|
async def integrations_page():
|
|
odoo_stats = {}
|
|
odoo_last_sync = "—"
|
|
odoo_error = ""
|
|
|
|
try:
|
|
snapshot = get_odoo_product_snapshot(limit=1)
|
|
odoo_stats = snapshot.get("stats") or {}
|
|
odoo_last_sync = odoo_stats.get("last_synced_at") or "—"
|
|
except Exception as exc:
|
|
odoo_error = str(exc)
|
|
|
|
def badge(enabled: bool) -> str:
|
|
if enabled:
|
|
return '<span class="badge bg-success-subtle text-success">Ativo</span>'
|
|
return '<span class="badge bg-secondary-subtle text-secondary">Inativo</span>'
|
|
|
|
odoo_enabled = bool(getattr(settings, "odoo_enabled", False))
|
|
jasmin_enabled = bool(getattr(settings, "jasmin_enabled", False))
|
|
packlink_enabled = bool(getattr(settings, "packlink_enabled", False))
|
|
chatwoot_enabled = bool(getattr(settings, "chatwoot_base_url", ""))
|
|
|
|
odoo_error_html = ""
|
|
if odoo_error:
|
|
odoo_error_html = f"""
|
|
<div class="alert alert-warning mt-3 mb-0">
|
|
<strong>Erro Odoo:</strong> {esc(odoo_error)}
|
|
</div>
|
|
"""
|
|
|
|
body = f"""
|
|
<div class="d-flex flex-wrap justify-content-between align-items-start gap-3 mb-4">
|
|
<div>
|
|
<div class="cf-eyebrow">Configuração</div>
|
|
<h1 class="h3 mb-1">Integrações</h1>
|
|
<p class="text-secondary mb-0">Ligações externas usadas pelo cockpit operacional.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3">
|
|
<div class="col-lg-6">
|
|
<section class="card cf-card">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-start mb-3">
|
|
<div>
|
|
<h2 class="cf-section-title mb-1">Odoo</h2>
|
|
<div class="small text-secondary">Produtos vendáveis, stock e estado operacional.</div>
|
|
</div>
|
|
{badge(odoo_enabled)}
|
|
</div>
|
|
|
|
<dl class="row small mb-3">
|
|
<dt class="col-5 text-secondary">Base URL</dt>
|
|
<dd class="col-7">{esc(getattr(settings, "odoo_base_url", "") or "—")}</dd>
|
|
|
|
<dt class="col-5 text-secondary">Base de dados</dt>
|
|
<dd class="col-7">{esc(getattr(settings, "odoo_db", "") or "—")}</dd>
|
|
|
|
<dt class="col-5 text-secondary">Produtos sincronizados</dt>
|
|
<dd class="col-7">{esc(odoo_stats.get("synced_products") or 0)}</dd>
|
|
|
|
<dt class="col-5 text-secondary">Com BOM</dt>
|
|
<dd class="col-7">{esc(odoo_stats.get("products_with_bom") or 0)}</dd>
|
|
|
|
<dt class="col-5 text-secondary">Com stock disponível</dt>
|
|
<dd class="col-7">{esc(odoo_stats.get("products_with_available_stock") or 0)}</dd>
|
|
|
|
<dt class="col-5 text-secondary">Último sync</dt>
|
|
<dd class="col-7">{esc(odoo_last_sync)}</dd>
|
|
</dl>
|
|
|
|
<div class="d-flex flex-wrap gap-2">
|
|
<form method="post" action="/integrations/odoo/test">
|
|
<button class="btn btn-outline-primary btn-sm" type="submit">Testar ligação</button>
|
|
</form>
|
|
<form method="post" action="/integrations/odoo/sync-products" hx-confirm="Sincronizar produtos vendáveis do Odoo agora? Esta ação pode atualizar o catálogo e deve ser usada conscientemente.">
|
|
<button class="btn btn-primary btn-sm" type="submit">Sincronizar produtos vendáveis</button>
|
|
</form>
|
|
</div>
|
|
|
|
{odoo_error_html}
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
<div class="col-lg-6">
|
|
<section class="card cf-card">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-start mb-3">
|
|
<div>
|
|
<h2 class="cf-section-title mb-1">Jasmin</h2>
|
|
<div class="small text-secondary">Orçamentos, faturas e documentos fiscais.</div>
|
|
</div>
|
|
{badge(jasmin_enabled)}
|
|
</div>
|
|
|
|
<dl class="row small mb-0">
|
|
<dt class="col-5 text-secondary">Base URL</dt>
|
|
<dd class="col-7">{esc(getattr(settings, "jasmin_base_url", "") or "—")}</dd>
|
|
|
|
<dt class="col-5 text-secondary">URL público</dt>
|
|
<dd class="col-7">{esc(getattr(settings, "jasmin_public_url", "") or "—")}</dd>
|
|
</dl>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
<div class="col-lg-6">
|
|
<section class="card cf-card">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-start mb-3">
|
|
<div>
|
|
<h2 class="cf-section-title mb-1">Packlink PRO</h2>
|
|
<div class="small text-secondary">Recolhas, envios, etiquetas e tracking.</div>
|
|
</div>
|
|
{badge(packlink_enabled)}
|
|
</div>
|
|
|
|
<dl class="row small mb-0">
|
|
<dt class="col-5 text-secondary">Base URL</dt>
|
|
<dd class="col-7">{esc(getattr(settings, "packlink_base_url", "") or "—")}</dd>
|
|
|
|
<dt class="col-5 text-secondary">URL público</dt>
|
|
<dd class="col-7">{esc(getattr(settings, "packlink_public_url", "") or "—")}</dd>
|
|
</dl>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
<div class="col-lg-6">
|
|
<section class="card cf-card">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-start mb-3">
|
|
<div>
|
|
<h2 class="cf-section-title mb-1">Chatwoot</h2>
|
|
<div class="small text-secondary">Comunicação com cliente.</div>
|
|
</div>
|
|
{badge(chatwoot_enabled)}
|
|
</div>
|
|
|
|
<dl class="row small mb-0">
|
|
<dt class="col-5 text-secondary">Base URL</dt>
|
|
<dd class="col-7">{esc(getattr(settings, "chatwoot_base_url", "") or "—")}</dd>
|
|
|
|
<dt class="col-5 text-secondary">URL público</dt>
|
|
<dd class="col-7">{esc(getattr(settings, "chatwoot_public_url", "") or "—")}</dd>
|
|
</dl>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
|
|
<section class="card cf-card mt-4">
|
|
<div class="card-body">
|
|
<h2 class="cf-section-title mb-2">Regra operacional</h2>
|
|
<p class="text-secondary mb-0">
|
|
O ClientFlow não replica Odoo, Jasmin ou Packlink. Apenas consulta e guarda o estado necessário
|
|
para decidir a próxima ação com o cliente.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
"""
|
|
|
|
return layout("Integrações", "Configuração das ligações externas", body, "integrations")
|
|
|
|
|
|
@router.post("/integrations/odoo/test")
|
|
async def integrations_odoo_test_action(request: Request):
|
|
try:
|
|
test_odoo_connection()
|
|
except Exception:
|
|
pass
|
|
return RedirectResponse(url="/integrations", status_code=303)
|
|
|
|
|
|
@router.post("/integrations/odoo/sync-products")
|
|
async def integrations_odoo_sync_products_action(request: Request):
|
|
try:
|
|
sync_odoo_products(limit=1000, include_inactive=True)
|
|
except Exception:
|
|
pass
|
|
return RedirectResponse(url="/integrations", status_code=303)
|
|
|
|
|