feat(web+api): contatos vinculados a clientes com sync ERP e base WhatsApp
DB:
- sar.vw_contatos: view sobre gestao.contato com join sig.corrent
(id_entidade CHAR20 = id_corrent; cod_vendedor para filtro por rep)
- sar.contatos_novos: staging com nome, cargo, departamento, telefone,
celular, whatsapp, email, dt_aniversario, anotacoes
- trg_contato_novo: AFTER INSERT → gestao.contato; grava id_contato_erp
API:
- GET /clients/:id/contacts — lista contatos ativos do cliente
- POST /clients/:id/contacts — cria via staging; retorna { idContatoErp, sincronizado }
Web:
- ClientContacts: tabela de contatos no detalhe do cliente
- Links diretos WhatsApp (wa.me) + mailto por contato
- Modal "Novo Contato" com todos os campos; feedback de código ERP no sucesso
- whatsapp é o campo principal para envio futuro via ChatWoot
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -503,6 +503,37 @@ SELECT
|
||||
nome
|
||||
FROM gestao.marca;
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- Contatos vinculados a clientes (gestao.contato)
|
||||
-- id_entidade = id_corrent::text (CHAR 20, auto-pad)
|
||||
-- Usado para envio via WhatsApp / ChatWoot futuramente
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE OR REPLACE VIEW sar.vw_contatos AS
|
||||
SELECT
|
||||
c.id_contato,
|
||||
c.id_empresa,
|
||||
TRIM(c.id_entidade)::integer AS id_corrent,
|
||||
c.id_chatwoot,
|
||||
TRIM(c.nome) AS nome,
|
||||
TRIM(c.empresa) AS empresa,
|
||||
TRIM(c.cargo) AS cargo,
|
||||
TRIM(c.departamento) AS departamento,
|
||||
c.dt_aniversario,
|
||||
TRIM(c.telefone) AS telefone,
|
||||
TRIM(c.ramal) AS ramal,
|
||||
TRIM(c.celular) AS celular,
|
||||
TRIM(c.whatsapp) AS whatsapp,
|
||||
TRIM(c.email) AS email,
|
||||
COALESCE(c.ativo, 1) AS ativo,
|
||||
c.anotacoes,
|
||||
TRIM(cr.nome) AS nome_cliente,
|
||||
TRIM(cr.razao::text) AS razao_cliente,
|
||||
cr.cod_vendedor
|
||||
FROM gestao.contato c
|
||||
LEFT JOIN sig.corrent cr
|
||||
ON cr.id_corrent = TRIM(c.id_entidade)::integer
|
||||
WHERE TRIM(c.id_entidade) ~ '^\d+$';
|
||||
|
||||
-- =============================================================================
|
||||
-- PARTE 2 — TABELAS DE ESCRITA DO SAR
|
||||
-- Dados gerados pelo SAR — não existem no ERP.
|
||||
|
||||
@@ -448,6 +448,97 @@ CREATE TRIGGER trg_cliente_novo
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION sar.fn_sync_cliente_to_erp();
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 7. Tabela staging de contatos criados pelo rep
|
||||
--
|
||||
-- id_entidade em gestao.contato é CHAR(20) — recebe id_corrent::text (auto-pad).
|
||||
-- whatsapp aqui é o número que será usado para envio via ChatWoot.
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS sar.contatos_novos (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
id_empresa INTEGER NOT NULL,
|
||||
cod_vendedor INTEGER NOT NULL,
|
||||
id_corrent INTEGER NOT NULL,
|
||||
|
||||
nome VARCHAR(100) NOT NULL,
|
||||
empresa VARCHAR(100),
|
||||
cargo VARCHAR(100),
|
||||
departamento VARCHAR(100),
|
||||
dt_aniversario DATE,
|
||||
telefone VARCHAR(20),
|
||||
ramal VARCHAR(10),
|
||||
celular VARCHAR(20),
|
||||
whatsapp VARCHAR(20),
|
||||
email VARCHAR(150),
|
||||
anotacoes TEXT,
|
||||
|
||||
id_contato_erp INTEGER,
|
||||
sincronizado BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
erro_sync TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_sar_ctnov_corrent ON sar.contatos_novos(id_corrent);
|
||||
CREATE INDEX IF NOT EXISTS idx_sar_ctnov_empresa ON sar.contatos_novos(id_empresa);
|
||||
CREATE INDEX IF NOT EXISTS idx_sar_ctnov_vend ON sar.contatos_novos(cod_vendedor);
|
||||
CREATE INDEX IF NOT EXISTS idx_sar_ctnov_sync ON sar.contatos_novos(sincronizado);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- 8. Função: replicar contato → gestao.contato
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE OR REPLACE FUNCTION sar.fn_sync_contato_to_erp()
|
||||
RETURNS TRIGGER LANGUAGE plpgsql AS $fn$
|
||||
DECLARE
|
||||
v_id_contato INTEGER;
|
||||
BEGIN
|
||||
BEGIN
|
||||
INSERT INTO gestao.contato (
|
||||
id_empresa, id_entidade,
|
||||
nome, empresa, cargo, departamento,
|
||||
dt_aniversario,
|
||||
telefone, ramal, celular, whatsapp, email,
|
||||
ativo, anotacoes
|
||||
) VALUES (
|
||||
NEW.id_empresa,
|
||||
NEW.id_corrent::text,
|
||||
COALESCE(LEFT(NEW.nome, 100), ''),
|
||||
LEFT(NEW.empresa, 100),
|
||||
LEFT(NEW.cargo, 100),
|
||||
LEFT(NEW.departamento, 100),
|
||||
NEW.dt_aniversario,
|
||||
LEFT(NEW.telefone, 20),
|
||||
LEFT(NEW.ramal, 10),
|
||||
LEFT(NEW.celular, 20),
|
||||
LEFT(NEW.whatsapp, 20),
|
||||
LEFT(NEW.email, 150),
|
||||
1,
|
||||
NEW.anotacoes
|
||||
)
|
||||
RETURNING id_contato INTO v_id_contato;
|
||||
|
||||
UPDATE sar.contatos_novos
|
||||
SET id_contato_erp = v_id_contato,
|
||||
sincronizado = TRUE,
|
||||
erro_sync = NULL
|
||||
WHERE id = NEW.id;
|
||||
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
UPDATE sar.contatos_novos
|
||||
SET sincronizado = FALSE,
|
||||
erro_sync = SQLERRM
|
||||
WHERE id = NEW.id;
|
||||
END;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$fn$;
|
||||
|
||||
-- 9. Trigger
|
||||
DROP TRIGGER IF EXISTS trg_contato_novo ON sar.contatos_novos;
|
||||
CREATE TRIGGER trg_contato_novo
|
||||
AFTER INSERT ON sar.contatos_novos
|
||||
FOR EACH ROW EXECUTE FUNCTION sar.fn_sync_contato_to_erp();
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Verificação final
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user