# Spec 023 — Cobrança recorrente & visibilidade Desk (empresa / billing) **Criado:** 2026-06-17 **Solicitado por:** Roger **Status:** Spec — implementação pendente (motor cobrança → ver **Spec 024** FOSSBilling) **Prioridade:** P1 (financeiro + operação) **Sistemas:** Wizard VM112 · Desk VM122 · Motor de cobrança (fase 2) **Módulo Desk:** `billing-recurrence` (novo — Spec 015) **Depende de:** Spec 012 (tickets onboarding), Spec 018 (Serviços / clientes), Spec 021 (webhooks) **Relacionado:** Card wizard «Dados da empresa e cobrança» (imagem Roger, 2026-06-17) --- ## Resumo Quando o utilizador **preenche e confirma** o card **«Dados da empresa e cobrança»** no wizard VM112, o Desk deve **reflectir imediatamente** o estado comercial/financeiro em três superfícies: 1. **Dashboard** — KPI + feed de sessões/tickets em fase billing 2. **Audit Overview** — domínio/tenant com badge «Empresa / Cobrança» 3. **Serviços** — ícone de **recorrência activa** no card do cliente + link para **conta do cliente** (ficha financeira) A fase 1 é **visibilidade e orquestração no Desk** (eventos, estados, links). A fase 2 liga o motor de **cobrança recorrente** (assinatura, boleto, PIX, inadimplência) ao `company_profile` já capturado. **Regra de ouro:** o wizard **não cobra** no passo empresa — apenas recolhe dados e emite `company.validated`. A recorrência **activa-se** após validação OPS + provisionamento do plano no motor financeiro. --- ## Problema | Hoje | Necessidade | |------|-------------| | Evento `company.validated` gera ticket `[billing-validation]` e `billing_state` no payload | Ops não vê **de relance** quantos clientes estão em cobrança pendente / activa | | Card empresa existe no wizard (CNPJ, morada, `email_billing`, `confirm_billing`) | Mesmo momento deve aparecer no **Dashboard**, **Overview** e **Serviços** | | Serviços (Spec 018) mostra só saúde técnica (e-mail tenant activo) | Falta indicador **💳 recorrência** e atalho para **conta do cliente** | | Sem motor de cobrança ligado ao Desk | Boletos, débitos e MRR dispersos ou manuais | --- ## Gatilho (wizard VM112) ### Momento UX O card **«Dados da empresa e cobrança»** é exibido após passos de conta/domínio (gate `company_gate`). O utilizador pode: - Preencher agora (`confirm_billing` + `confirm_accurate`) - Adiar — política ainda não registada na sessão (banner azul na imagem) ### Webhook (já parcialmente implementado) ```json { "event": "company.validated", "domain": "myvexx.com", "session_id": "", "data": { "billing_state": "awaiting_billing_validation", "company_profile": { "trade_name": "Myvexx", "legal_name": "Myvexx Ltda", "tax_id_type": "cnpj", "tax_id": "00000000000191", "email_billing": "financeiro@myvexx.com", "payment_method": "", "confirm_billing": true, "address": { "country": "BR", "city": "...", "postal_code": "..." } } } } ``` ### Estados `billing_state` (Desk — normalizar) | Estado | Significado | UI | |--------|-------------|-----| | `policy_pending` | Card visto mas empresa ainda não confirmada | Cinza — «Política pendente» | | `awaiting_billing_validation` | `company.validated` — aguarda OPS | Âmbar — «Validar cobrança» | | `billing_active` | Plano + recorrência criados no motor financeiro | Verde — «Recorrência activa» | | `billing_paused` | Suspenso manualmente (inadimplência / pedido cliente) | Vermelho suave | | `billing_cancelled` | Cancelado — manter histórico | Cinza riscado | Transição inicial automática no webhook: → `awaiting_billing_validation`. --- ## Superfícies Desk (fase 1 — visibilidade) ### 1. Dashboard | Elemento | Comportamento | |----------|---------------| | **KPI «Cobrança pendente»** | Contagem tickets/sessões com `billing_state = awaiting_billing_validation` (48h) | | **KPI «Recorrência activa»** | Contagem clientes com `billing_active` | | **Sessões activas** | Badge `billing` no card quando `current_stage >= company_validated` | | **Tickets recentes** | Prefixo `[billing-validation]` já existe — destacar com ícone 💳 | | **Feed rápido** | Últimos 5 `company.validated` com domínio + razão social (mascarar CNPJ para NOC) | ### 2. Audit Overview | Elemento | Comportamento | |----------|---------------| | **Domínio na lista** | Badge «Empresa» quando funil ≥ `company_validated` | | **Modal domínio** | Secção **Cobrança** com: estado, `trade_name`, `email_billing` (mascarado NOC), data confirmação | | **Card tenant VM112** | Contador «X domínios em validação billing» nas últimas 24h | ### 3. Serviços (`overview-home` — Spec 018) | Elemento | Comportamento | |----------|---------------| | **Linha do cliente** | Ícone pequeno **💳** ou `servicos-billing-dot--active` quando `billing_active` | | **Tooltip** | «Recorrência activa — clique para conta do cliente» | | **Clique no ícone** | Abre **ficha Conta do cliente** (drawer/modal) — não confundir com tile E-mail Tenant | | **Stats row** | Novo contador: «N recorrências activas» | ### Ficha «Conta do cliente» (nova — v1) Painel lateral ou modal com: - Dados empresa (`company_profile` — RBAC Spec 003) - Estado billing + link externo motor financeiro (Odoo partner / Lago customer) quando fase 2 - Histórico: `company.validated`, activação recorrência, últimos pagamentos (webhook fase 2) - Acções OPS (fase 1): «Marcar validado», «Activar recorrência» (manual) - Acções OPS (fase 2): «Criar assinatura», «Reenviar boleto», «Suspender por inadimplência» --- ## Modelo de dados Desk (fase 1) ### Tabela `billing_accounts` (nova) ```sql CREATE TABLE billing_accounts ( id INTEGER PRIMARY KEY, domain TEXT NOT NULL, session_id TEXT, ticket_id INTEGER, tax_id TEXT, legal_name TEXT, trade_name TEXT, email_billing TEXT, company_profile_json TEXT, billing_state TEXT NOT NULL DEFAULT 'awaiting_billing_validation', recurrence_active INTEGER NOT NULL DEFAULT 0, external_customer_id TEXT, -- Odoo res.partner id ou Lago external_id external_subscription_id TEXT, payment_provider TEXT, -- asaas | iugu | stripe | manual plan_code TEXT, -- email_tenant_monthly, etc. activated_at TEXT, activated_by TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE UNIQUE INDEX idx_billing_domain ON billing_accounts(domain); ``` ### Upsert no webhook Em `_process_ingress`, após `company.validated`: 1. `billing_store.upsert_from_company_validated(...)` 2. Ligar `ticket_id` e `webhook_event_id` 3. Não activar `recurrence_active` até confirmação OPS ou webhook do motor financeiro ### API (fase 1) | Método | Rota | Descrição | |--------|------|-----------| | GET | `/api/v1/billing/accounts` | Lista (filtro `billing_state`, `domain`) | | GET | `/api/v1/billing/accounts/{id}` | Ficha conta cliente | | GET | `/api/v1/billing/accounts/by-domain/{domain}` | Lookup para Serviços | | PATCH | `/api/v1/billing/accounts/{id}` | OPS: mudar estado, `recurrence_active` | | GET | `/api/v1/billing/summary` | KPIs dashboard | --- ## Motor de cobrança recorrente (fase 2) ### Requisitos Ligbox (Brasil) - Assinatura mensal/anual por domínio ou por utilizador - **Boleto** + **PIX** + cartão (futuro) - CNAB retorno / conciliação bancária - NFSe / nota fiscal de serviço (quando aplicável) - Inadimplência: lembretes, suspensão serviço, dunning - API para o Desk criar cliente + plano a partir de `company_profile` ### Comparativo — aderência para o caso Ligbox | Critério | **Odoo 16** (+ OCA Brasil) | **Lago** (getlago/lago) | |----------|---------------------------|-------------------------| | Já na stack Roger | ✅ API V16 existente (`813f08e7…`) | ❌ Novo deploy | | Boleto nativo BR | ✅ `l10n_br_account_payment_brcobranca` (OCA) + CNAB | ⚠️ Via **Stripe** (`boleto` em BRL) — não CNAB tradicional | | PIX | ✅ Módulos ASAAS / Iugu / PagBank | ⚠️ Stripe PIX (se configurado) | | NFSe / fiscal BR | ✅ OCA `l10n_br_*` + integradores | ❌ Não é ERP fiscal | | Assinatura recorrente | ✅ `sale_subscription` + ASAAS Subscriptions | ✅ Core product — excelente API | | Metering (por caixa, GB) | ⚠️ Possível, menos natural | ✅ Event-based — ideal | | Controle débitos / aging | ✅ Contabilidade + follow-up pagamentos | ✅ Dunning + invoices | | Self-host | ✅ Já conhecido | ✅ Docker (Railway/K8s) | | Integração Desk | JSON-RPC / REST Odoo | REST API limpa | | Curva para MSP BR | **Menor** — um sistema fiscal + cobrança | **Maior** se precisar Odoo à parte para NF | ### Recomendação (Roger) **Aderência principal: Odoo 16** — já tens instância e API; o ecossistema **OCA Brasil** cobre boleto/CNAB, e módulos **ASAAS** ou **Iugu** fecham PIX + assinatura recorrente + webhooks de pagamento sem reinventar fiscal. **Lago** é excelente como **motor de subscrição/metering** (preço por utilizador, usage API) se no futuro quiseres billing estilo SaaS puro **desacoplado** do fiscal — mas para **boletos, débitos e conformidade BR**, continuarias a precisar de Odoo (ou similar) em paralelo. #### Arquitectura sugerida ``` Wizard VM112 ──webhook──► Desk VM122 ──orquestra──► Odoo 16 │ │ │ company.validated billing_accounts res.partner company_profile UI Dashboard/Overview/ sale.subscription Serviços + ícone 💳 ASAAS/Iugu (boleto/PIX) webhooks → Desk (fase 2) ``` **Opção híbrida (fase 3+):** Lago calcula usage (caixas extra, storage) → Odoo emite NF + boleto consolidado. Só vale a pena com volume e pricing complexo. #### Projetos GitHub de referência | Projeto | Uso | |---------|-----| | [odoo/odoo](https://github.com/odoo/odoo) | Core ERP + Subscriptions | | [OCA/l10n-brazil](https://github.com/OCA/l10n-brazil) | Localização fiscal BR | | [getlago/lago](https://github.com/getlago/lago) | Metering + subscriptions API (complementar) | | ASAAS / Iugu Odoo modules | Pagamentos BR (boleto, PIX, recorrência) | --- ## Webhooks fase 2 (motor → Desk) | Evento | Acção Desk | |--------|------------| | `billing.subscription.created` | `recurrence_active=1`, `billing_state=billing_active` | | `billing.invoice.paid` | Registo pagamento; ícone 💳 verde | | `billing.invoice.overdue` | Badge inadimplência; ticket automático | | `billing.subscription.cancelled` | `billing_cancelled` | Fonte: Odoo (sale.subscription + payment transaction) ou ASAAS webhooks via worker Desk. --- ## RBAC (Spec 003) | Acção | super_admin | ops_lead | technician | noc | |-------|:-----------:|:--------:|:----------:|:---:| | Ver KPI billing dashboard | ✅ | ✅ | ✅ | ✅ (contagens) | | Ver `company_profile` completo | ✅ | ✅ | ✅ | ❌ mascarado | | Abrir conta do cliente | ✅ | ✅ | ✅ | ❌ | | Activar / suspender recorrência | ✅ | ✅ | ❌ | ❌ | | Link motor financeiro | ✅ | ✅ | ✅ | ❌ | --- ## UI — ícone recorrência (Serviços) ```html 💳 ``` CSS: bolinha verde 8px ou emoji discreto à direita do nome; `cursor:pointer`; separado do badge «activo» técnico do e-mail tenant. --- ## Fora de escopo v1 - Emissão real de boleto/NF (fase 2) - Portal do cliente pagar fatura (fase 3) - Multi-moeda - Pricing dinâmico usage-based (Lago — fase 3) --- ## Critérios de aceite (fase 1) 1. Webhook `company.validated` cria/atualiza `billing_accounts` 2. Dashboard mostra KPI «Cobrança pendente» > 0 após teste wizard 3. Overview mostra badge «Empresa» no domínio 4. Serviços: ícone 💳 só quando `recurrence_active=1`; clique abre ficha conta 5. NOC não vê CNPJ completo nem `email_billing` 6. Módulo `billing-recurrence` activável em Módulos Desk --- ## Plano de implementação | Fase | Entrega | |------|---------| | **1a** | `billing_store` + routes + hook webhook + API summary | | **1b** | Dashboard KPI + badges sessão/ticket | | **1c** | Overview badge + secção cobrança no modal | | **1d** | Serviços: ícone + modal conta cliente + PATCH manual OPS | | **2** | Integração Odoo: `res.partner` + subscription ASAAS | | **3** | Webhooks pagamento + inadimplência + suspensão serviço |