feat(infra): triggers ERP + reset migrations Prisma para schema correto

Triggers SAR→ERP (scripts/sar-triggers.sql):
- trg_pedido_aprovado: ao aprovar (situa 1→2) em sar.pedidos, replica
  automaticamente em sig.pedidos + sig.peditens e grava erp_id_pedido
- trg_cliente_novo: INSERT em sar.clientes_novos replica em sig.corrent
  com todos os campos obrigatórios; grava id_corrent_erp e flag sincronizado
- Corrige trigger tsvectorupdate do ERP para PG 18 (bpchar→text cast)
- Adiciona coluna erp_id_pedido em sar.pedidos e tabela sar.clientes_novos

Migrations Prisma:
- Remove 5 migrations obsoletas com nomes PascalCase (Order, Client, etc.)
  que não batiam com os @@map snake_case do schema atual
- Cria baseline 20260624000000_init_baseline apontando para estado correto

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-24 15:07:54 +00:00
parent 6f16dc8274
commit 35d0ba68d6
8 changed files with 496 additions and 243 deletions

View File

@@ -1,41 +0,0 @@
-- CreateEnum
CREATE TYPE "FinancialStatus" AS ENUM ('regular', 'attention', 'blocked');
-- CreateTable
CREATE TABLE "Client" (
"id" UUID NOT NULL,
"name" TEXT NOT NULL,
"tradeName" TEXT,
"taxId" TEXT NOT NULL,
"email" TEXT,
"phone" TEXT,
"address" JSONB,
"financialStatus" "FinancialStatus" NOT NULL DEFAULT 'regular',
"creditLimit" DECIMAL(15,2),
"repId" TEXT NOT NULL,
"lastOrderAt" TIMESTAMP(3),
"lastOrderValue" DECIMAL(15,2),
"openOrdersCount" INTEGER NOT NULL DEFAULT 0,
"erpCode" TEXT,
"syncedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3),
CONSTRAINT "Client_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Client_taxId_key" ON "Client"("taxId");
-- CreateIndex
CREATE INDEX "Client_repId_idx" ON "Client"("repId");
-- CreateIndex
CREATE INDEX "Client_taxId_idx" ON "Client"("taxId");
-- CreateIndex
CREATE INDEX "Client_name_idx" ON "Client"("name");
-- CreateIndex
CREATE INDEX "Client_deletedAt_idx" ON "Client"("deletedAt");

View File

@@ -1,95 +0,0 @@
-- CreateEnum
CREATE TYPE "OrderStatus" AS ENUM ('budget', 'pending_approval', 'approved', 'invoiced', 'cancelled');
-- CreateTable
CREATE TABLE "Order" (
"id" UUID NOT NULL,
"number" TEXT NOT NULL,
"clientId" UUID NOT NULL,
"repId" TEXT NOT NULL,
"status" "OrderStatus" NOT NULL DEFAULT 'budget',
"discountPct" DECIMAL(5,2) NOT NULL DEFAULT 0,
"subtotal" DECIMAL(15,2) NOT NULL,
"total" DECIMAL(15,2) NOT NULL,
"notes" TEXT,
"approvedById" TEXT,
"approvedAt" TIMESTAMP(3),
"invoicedAt" TIMESTAMP(3),
"cancelledAt" TIMESTAMP(3),
"idempotencyKey" TEXT,
"issuedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3),
CONSTRAINT "Order_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "OrderItem" (
"id" UUID NOT NULL,
"orderId" UUID NOT NULL,
"productCode" TEXT NOT NULL,
"productName" TEXT NOT NULL,
"quantity" DECIMAL(10,3) NOT NULL,
"unitPrice" DECIMAL(15,2) NOT NULL,
"discountPct" DECIMAL(5,2) NOT NULL DEFAULT 0,
"subtotal" DECIMAL(15,2) NOT NULL,
CONSTRAINT "OrderItem_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "OrderStatusHistory" (
"id" UUID NOT NULL,
"orderId" UUID NOT NULL,
"fromStatus" "OrderStatus",
"toStatus" "OrderStatus" NOT NULL,
"changedById" TEXT NOT NULL,
"note" TEXT,
"changedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "OrderStatusHistory_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Order_number_key" ON "Order"("number");
-- CreateIndex
CREATE UNIQUE INDEX "Order_idempotencyKey_key" ON "Order"("idempotencyKey");
-- CreateIndex
CREATE INDEX "Order_clientId_idx" ON "Order"("clientId");
-- CreateIndex
CREATE INDEX "Order_repId_idx" ON "Order"("repId");
-- CreateIndex
CREATE INDEX "Order_status_idx" ON "Order"("status");
-- CreateIndex
CREATE INDEX "Order_issuedAt_idx" ON "Order"("issuedAt");
-- CreateIndex
CREATE INDEX "Order_number_idx" ON "Order"("number");
-- CreateIndex
CREATE INDEX "Order_deletedAt_idx" ON "Order"("deletedAt");
-- CreateIndex
CREATE INDEX "OrderItem_orderId_idx" ON "OrderItem"("orderId");
-- CreateIndex
CREATE INDEX "OrderStatusHistory_orderId_idx" ON "OrderStatusHistory"("orderId");
-- CreateIndex
CREATE INDEX "OrderStatusHistory_changedAt_idx" ON "OrderStatusHistory"("changedAt");
-- AddForeignKey
ALTER TABLE "Order" ADD CONSTRAINT "Order_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "Client"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderItem" ADD CONSTRAINT "OrderItem_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderStatusHistory" ADD CONSTRAINT "OrderStatusHistory_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -1,52 +0,0 @@
-- AlterTable
ALTER TABLE "OrderItem" ADD COLUMN "productCategory" TEXT NOT NULL DEFAULT 'geral';
-- CreateTable
CREATE TABLE "Product" (
"id" UUID NOT NULL,
"code" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"category" TEXT NOT NULL DEFAULT 'geral',
"unitPrice" DECIMAL(15,2) NOT NULL,
"stock" DECIMAL(10,3),
"active" BOOLEAN NOT NULL DEFAULT true,
"erpCode" TEXT,
"syncedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3),
CONSTRAINT "Product_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "RepDiscountLimit" (
"repId" TEXT NOT NULL,
"category" TEXT NOT NULL,
"limit" DECIMAL(5,2) NOT NULL DEFAULT 5,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "RepDiscountLimit_pkey" PRIMARY KEY ("repId","category")
);
-- CreateIndex
CREATE UNIQUE INDEX "Product_code_key" ON "Product"("code");
-- CreateIndex
CREATE INDEX "Product_code_idx" ON "Product"("code");
-- CreateIndex
CREATE INDEX "Product_name_idx" ON "Product"("name");
-- CreateIndex
CREATE INDEX "Product_category_idx" ON "Product"("category");
-- CreateIndex
CREATE INDEX "Product_active_idx" ON "Product"("active");
-- CreateIndex
CREATE INDEX "Product_deletedAt_idx" ON "Product"("deletedAt");
-- CreateIndex
CREATE INDEX "RepDiscountLimit_repId_idx" ON "RepDiscountLimit"("repId");

View File

@@ -1,15 +0,0 @@
-- CreateTable
CREATE TABLE "RepTarget" (
"repId" TEXT NOT NULL,
"year" INTEGER NOT NULL,
"month" INTEGER NOT NULL,
"targetAmount" DECIMAL(15,2) NOT NULL,
"commissionRate" DECIMAL(5,2) NOT NULL DEFAULT 3,
"flexRate" DECIMAL(5,2) NOT NULL DEFAULT 1,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "RepTarget_pkey" PRIMARY KEY ("repId","year","month")
);
-- CreateIndex
CREATE INDEX "RepTarget_repId_idx" ON "RepTarget"("repId");

View File

@@ -1,22 +0,0 @@
-- CreateTable
CREATE TABLE "PushSubscription" (
"id" UUID NOT NULL,
"userId" TEXT NOT NULL,
"role" TEXT NOT NULL,
"endpoint" TEXT NOT NULL,
"p256dh" TEXT NOT NULL,
"auth" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "PushSubscription_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "PushSubscription_endpoint_key" ON "PushSubscription"("endpoint");
-- CreateIndex
CREATE INDEX "PushSubscription_userId_idx" ON "PushSubscription"("userId");
-- CreateIndex
CREATE INDEX "PushSubscription_role_idx" ON "PushSubscription"("role");

View File

@@ -1,17 +1,13 @@
import { Avatar, Badge, Button, Dropdown, Flex, Input, Typography } from 'antd';
import { Avatar, Badge, Button, Dropdown, Flex, Typography } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faBell,
faMagnifyingGlass,
faBars,
faRightFromBracket,
} from '@fortawesome/free-solid-svg-icons';
import { faBell, faBars, faBuilding, faRightFromBracket } from '@fortawesome/free-solid-svg-icons';
import { useNavigate } from '@tanstack/react-router';
import { brandTokens } from '../../lib/theme';
import { FoundationStatus } from './FoundationStatus';
import { usePendingCount } from '../../lib/queries/notifications';
import { useCurrentUser } from '../../lib/queries/auth';
import { useCompany } from '../../lib/queries/company';
import { authStore } from '../../lib/auth-store';
interface TopbarProps {
@@ -33,6 +29,11 @@ export function Topbar({ onToggleSidebar }: TopbarProps) {
const { data: pendingData } = usePendingCount();
const pendingCount = pendingData?.count ?? 0;
const { data: user } = useCurrentUser();
const { data: company } = useCompany();
const nomeFantasia = company?.nomeFantasia?.trim() ?? null;
const razaoSocial = company?.razaoSocial?.trim() ?? null;
const nomeEmpresa = nomeFantasia ?? razaoSocial ?? '—';
const mostrarRazao = nomeFantasia && razaoSocial && nomeFantasia !== razaoSocial;
const initials = user?.nome
? user.nome
.split(' ')
@@ -121,17 +122,39 @@ export function Topbar({ onToggleSidebar }: TopbarProps) {
</Flex>
</Flex>
{/* Centro: search (Supervisor/Admin) */}
<Flex flex={1} justify="center" style={{ maxWidth: 480, margin: '0 var(--space-2xl)' }}>
<Input
size="large"
placeholder="Buscar cliente, pedido, produto..."
prefix={
<FontAwesomeIcon icon={faMagnifyingGlass} style={{ color: 'var(--text-muted)' }} />
}
style={{ borderRadius: 12 }}
aria-label="Buscar"
/>
{/* Centro: empresa ativa */}
<Flex flex={1} justify="center" style={{ margin: '0 var(--space-2xl)' }}>
<Flex align="center" gap={10}>
<FontAwesomeIcon
icon={faBuilding}
style={{ color: 'var(--text-muted)', fontSize: 16, flexShrink: 0 }}
/>
<Flex vertical gap={0}>
<Typography.Text
style={{
fontSize: 'var(--text-xl)',
fontWeight: 'var(--font-weight-semibold)',
color: 'var(--text-main)',
lineHeight: 1.2,
whiteSpace: 'nowrap',
}}
>
{nomeEmpresa}
</Typography.Text>
{mostrarRazao && (
<Typography.Text
style={{
fontSize: 'var(--text-2xs)',
color: 'var(--text-muted)',
lineHeight: 1.3,
whiteSpace: 'nowrap',
}}
>
{razaoSocial}
</Typography.Text>
)}
</Flex>
</Flex>
</Flex>
{/* Lado direito: novo pedido + status fundação + notificações + perfil */}

455
scripts/sar-triggers.sql Normal file
View File

@@ -0,0 +1,455 @@
-- =============================================================================
-- sar-triggers.sql
-- Triggers de integração SAR → ERP (schema sig/gestao no banco libreplast)
--
-- Execução: psql -U postgres -d libreplast -f scripts/sar-triggers.sql
--
-- CONTEÚDO:
-- 1. ALTER TABLE sar.pedidos — adiciona erp_id_pedido para rastrear ref ERP
-- 2. fn_sync_pedido_aprovado_to_erp — replica pedido aprovado em sig.pedidos
-- 3. trg_pedido_aprovado — dispara fn acima ao aprovar (situa 1→2)
-- 4. sar.clientes_novos — tabela staging de clientes criados pelo rep
-- 5. fn_sync_cliente_to_erp — replica cliente em sig.corrent
-- 6. trg_cliente_novo — dispara fn acima no INSERT em clientes_novos
-- =============================================================================
-- ---------------------------------------------------------------------------
-- 1. Coluna de referência ERP no pedido SAR
-- ---------------------------------------------------------------------------
ALTER TABLE sar.pedidos
ADD COLUMN IF NOT EXISTS erp_id_pedido INTEGER;
CREATE INDEX IF NOT EXISTS idx_sar_ped_erp_id ON sar.pedidos(erp_id_pedido)
WHERE erp_id_pedido IS NOT NULL;
-- ---------------------------------------------------------------------------
-- 2. Função: replicar pedido aprovado → sig.pedidos + sig.peditens
--
-- Dispara quando sar.pedidos.situa muda de qualquer valor para 2 (Aprovado).
-- Insere em sig.pedidos com situa=1 (Pendente ERP) para que o operador
-- da fábrica finalize o lançamento. Grava o id_pedido ERP de volta em
-- sar.pedidos.erp_id_pedido.
--
-- id_tes=48 (Venda de Produção) — padrão SAR. Mudar para 53 se for bonificação.
-- numero: MAX+1 por empresa — mesmo padrão usado pelo SIG Desktop.
-- ---------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION sar.fn_sync_pedido_aprovado_to_erp()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
DECLARE
v_numero INTEGER;
v_id_pedido_erp INTEGER;
v_cod_pauta INTEGER;
v_item RECORD;
v_id_st INTEGER;
v_endereco TEXT;
v_bairro TEXT;
v_municipio TEXT;
v_estado TEXT;
v_cep TEXT;
BEGIN
-- Só dispara na transição → 2 (Aprovado) e se ainda não sincronizado
IF NEW.situa <> 2 OR OLD.situa = 2 OR NEW.erp_id_pedido IS NOT NULL THEN
RETURN NEW;
END IF;
-- Próximo número disponível para a empresa (com lock)
SELECT COALESCE(MAX(numero), 0) + 1
INTO v_numero
FROM sig.pedidos
WHERE id_empresa = NEW.id_empresa
FOR UPDATE;
-- Código da pauta (sig usa codigo, SAR armazena id_pauta)
SELECT codigo INTO v_cod_pauta
FROM gestao.pauta
WHERE id_pauta = NEW.id_pauta
LIMIT 1;
-- Endereço de entrega do cliente (do ERP)
SELECT
COALESCE(c.endereco, ''),
COALESCE(c.bairr, ''),
COALESCE(m.nome, ''),
COALESCE(m.estado, ''),
COALESCE(c.cep, '')
INTO v_endereco, v_bairro, v_municipio, v_estado, v_cep
FROM sig.corrent c
LEFT JOIN gestao.municipio m ON m.id_municipio = c.id_municipio
WHERE c.id_corrent = NEW.id_cliente
LIMIT 1;
-- Inserir cabeçalho do pedido no ERP
INSERT INTO sig.pedidos (
id_empresa,
tipo,
numero,
data,
data_emissao,
num_ped_vendedor,
id_tes,
situa,
id_local,
clien,
cod_vendedor,
cod_pauta,
cod_formapag,
e_ender,
e_bairr,
e_munic,
e_estad,
e_cep,
conta,
totpr,
ipi,
vl_icms_subst,
total,
descp,
descv,
com_fat,
com_rec,
obs,
prz_con,
tx_acrescimo,
num_ped_sar,
inf_usuario,
fconta,
frete,
cliente_retira,
pesob,
pesol,
ordem,
finalidade,
vol_qtd,
vol_especie,
vol_marca,
vol_numero,
obs_nf1,
obs_nf2,
num_oc,
usuario_credito,
num_lacre,
etiqueta_emitida,
canc_saldo,
rec_antecipado,
guid_sarped,
redesp_frete,
prioridade,
classificacao,
enviar_email,
tp_moeda,
vl_cotacao
) VALUES (
NEW.id_empresa,
'P',
v_numero,
NEW.dt_pedido,
NEW.dt_pedido,
NEW.num_ped_sar, -- num_ped_vendedor = SAR ref
48, -- id_tes: Venda de Produção
1, -- situa ERP: Pendente (operador finaliza)
0,
NEW.id_cliente,
NEW.cod_vendedor,
COALESCE(v_cod_pauta, 0),
COALESCE(NEW.cod_formapag, 0),
COALESCE(v_endereco, ''),
COALESCE(v_bairro, ''),
COALESCE(v_municipio, ''),
COALESCE(v_estado, ''),
COALESCE(v_cep, ''),
'',
NEW.total_produtos,
NEW.total_ipi,
NEW.total_icmsst,
NEW.total,
NEW.desconto_perc,
NEW.desconto_valor,
NEW.comissao,
0,
COALESCE(LEFT(NEW.obs, 1024), ''),
0,
COALESCE(NEW.acrescimo, 0),
NEW.num_ped_sar,
1, -- inf_usuario=1: criado pelo SAR
0,
0,
0,
0,
0,
0,
' ',
'',
'',
'',
'',
'',
'',
' ',
'',
'',
0,
0,
0,
'',
0,
2,
' ',
0,
0,
0
)
RETURNING id_pedido INTO v_id_pedido_erp;
-- Inserir itens no ERP
FOR v_item IN
SELECT pi.*
FROM sar.pedido_itens pi
WHERE pi.id_pedido = NEW.id
ORDER BY pi.ordem
LOOP
-- Situação tributária do produto
SELECT s.id_st INTO v_id_st
FROM gestao.produto p
JOIN gestao.st s ON s.codigo = p.cod_st
WHERE p.id_erp = v_item.id_produto
AND p.id_empresa = CASE
WHEN NEW.id_empresa > 9000
THEN NEW.id_empresa - 9000
ELSE NEW.id_empresa
END
LIMIT 1;
INSERT INTO sig.peditens (
id_pedido,
ordem,
produ,
id_grade,
qtd,
pruni,
descp,
descv,
ipi,
total,
comis,
obs,
id_st,
preco_pauta,
qtd_vasilhame_dev,
id_integrado,
qtd_bonificacao,
num_entrega,
vl_comissao_und,
id_acabamento,
id_vidro,
largura,
altura,
l1, l2, l3, l4,
h1, h2, h3, h4,
id_varprod,
id_vista,
num_op,
status,
vl_icmsst,
tp_operacao,
vl_acrescimo,
tempo_prod_prev,
preco_ipi,
vl_flex
) VALUES (
v_id_pedido_erp,
v_item.ordem,
v_item.id_produto,
NULL,
v_item.qtd,
v_item.preco_unitario,
v_item.desconto_perc,
v_item.desconto_valor,
v_item.vl_ipi,
v_item.total,
v_item.comissao,
'',
COALESCE(v_id_st, 0),
v_item.preco_pauta,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0,
0,
0,
1, -- status=1: item ativo/normal
v_item.vl_icmsst,
0,
0,
0,
v_item.preco_com_ipi,
v_item.vl_flex
);
END LOOP;
-- Gravar referência ERP de volta no pedido SAR
UPDATE sar.pedidos
SET erp_id_pedido = v_id_pedido_erp
WHERE id = NEW.id;
RETURN NEW;
END;
$$;
-- Criar (ou recriar) o trigger
DROP TRIGGER IF EXISTS trg_pedido_aprovado ON sar.pedidos;
CREATE TRIGGER trg_pedido_aprovado
AFTER UPDATE OF situa ON sar.pedidos
FOR EACH ROW
EXECUTE FUNCTION sar.fn_sync_pedido_aprovado_to_erp();
-- ---------------------------------------------------------------------------
-- 4. Tabela staging de clientes novos criados pelo SAR
--
-- O rep preenche os campos obrigatórios e o trigger replica em sig.corrent.
-- Após a replicação, id_corrent_erp é preenchido com o id gerado pelo ERP.
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS sar.clientes_novos (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
id_empresa INTEGER NOT NULL,
cod_vendedor INTEGER NOT NULL,
-- Dados principais
nome VARCHAR(40) NOT NULL,
razao VARCHAR(65) NOT NULL,
pesso SMALLINT NOT NULL DEFAULT 1, -- 0=PJ 1=PF
consfinal SMALLINT NOT NULL DEFAULT 0,
cgcpf VARCHAR(18),
suf_cgcpf VARCHAR(3) NOT NULL DEFAULT '',
inscr VARCHAR(18) NOT NULL DEFAULT '',
indicador_ie SMALLINT, -- 1=contribuinte 2=isento 9=não contribuinte
-- Endereço
endereco VARCHAR(60) NOT NULL DEFAULT '',
num_endereco VARCHAR(10) NOT NULL DEFAULT '',
bairro VARCHAR(60) NOT NULL DEFAULT '',
id_municipio INTEGER NOT NULL,
cep VARCHAR(9) NOT NULL DEFAULT '',
-- Contato
ddd VARCHAR(4) NOT NULL DEFAULT '',
telefone VARCHAR(35) NOT NULL DEFAULT '',
email VARCHAR(80) NOT NULL DEFAULT '',
-- Comercial
limite_credito NUMERIC(15,2) NOT NULL DEFAULT 0,
cod_formapag INTEGER,
cod_pauta INTEGER,
-- Resultado após sync
id_corrent_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_clinovos_empresa ON sar.clientes_novos(id_empresa);
CREATE INDEX IF NOT EXISTS idx_sar_clinovos_vend ON sar.clientes_novos(cod_vendedor);
CREATE INDEX IF NOT EXISTS idx_sar_clinovos_sync ON sar.clientes_novos(sincronizado);
-- ---------------------------------------------------------------------------
-- 5. Função: replicar cliente novo → sig.corrent
--
-- Campos obrigatórios NOT NULL sem default em sig.corrent recebem '' (char)
-- ou 0 (int). O ERP aceita esses defaults — é o mesmo padrão do SIG Desktop
-- ao cadastrar cliente mínimo.
-- ---------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION sar.fn_sync_cliente_to_erp()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
DECLARE
v_id_corrent INTEGER;
BEGIN
BEGIN
INSERT INTO sig.corrent (
ativo, pesso, consfinal, cgcpf, suf_cgcpf, inscr, nome, razao,
endereco, cxpostal, compl, bairr, id_municipio, cep,
e_ender, e_bairr, e_cep, c_ender, c_bairr, c_cep,
e_mail, grupo_eco, no_reg, hor_descarga,
fat_anul, cap_social, cap_realiz, cap_aberto, con_ac_privado, con_ac_nacional,
ramo_ativ, cod_ativ_ir, no_empregados,
importa, p_importa, exporta, p_exporta,
princ_produ1, princ_produ2, princ_produ3, perc_vendas1, perc_vendas2, perc_vendas3,
www, banc1, banc2, banc3, agen1, agen2, agen3, nagen1, nagen2, nagen3,
ct_nu, ct_se, ci1, ci2, orgex, uf,
ddd, telef, fax, data, cod_vendedor, conta,
c_dm_nascto, c_fone, con_f, f_dm_nascto, f_fone, c_ctb, c_ctb_consig,
-- campos pessoais (obrigatórios sem default)
sexo, e_civ, pai, mae, conju, ocupa, c_spc, natur,
t_ser, l_tra, t_end, inscr_pro, classe,
tolatr, inscr_suframa, lei_livre, codigo,
aviso_protesto, codigo_vend_prep, id_tipo,
i_ban, i_com, obs,
-- comercial
limcred, indicador_ie, cod_formapag, cod_pauta
) VALUES (
1, NEW.pesso, NEW.consfinal,
COALESCE(NEW.cgcpf, ''), COALESCE(NEW.suf_cgcpf, ''), COALESCE(NEW.inscr, ''),
COALESCE(LEFT(NEW.nome, 40), ''), LEFT(NEW.razao, 65),
COALESCE(LEFT(NEW.endereco, 60), ''), 0, '',
COALESCE(LEFT(NEW.bairro, 60), ''), NEW.id_municipio, COALESCE(NEW.cep, ''),
COALESCE(LEFT(NEW.endereco, 60), ''), COALESCE(LEFT(NEW.bairro, 60), ''), COALESCE(NEW.cep, ''),
COALESCE(LEFT(NEW.endereco, 60), ''), COALESCE(LEFT(NEW.bairro, 60), ''), COALESCE(NEW.cep, ''),
COALESCE(LEFT(NEW.email, 80), ''), '', '', '',
0, 0, 0, 0, 0, 0,
'', 0, 0,
0, 0, 0, 0,
'', '', '', 0, 0, 0,
'', 0, 0, 0, '', '', '', '', '', '',
0, 0, '', 0, '', '',
COALESCE(NEW.ddd, ''), COALESCE(LEFT(NEW.telefone, 35), ''), '', CURRENT_DATE,
NEW.cod_vendedor, '',
NULL, '', '', NULL, '', 0, 0,
-- campos pessoais com defaults neutros
0, 0, '', '', '', '', '', '',
0, '', 0, '', '',
0, '', '', '',
0, 0, 0,
'', '', '',
-- comercial
COALESCE(NEW.limite_credito, 0), NEW.indicador_ie, NEW.cod_formapag, NEW.cod_pauta
)
RETURNING id_corrent INTO v_id_corrent;
UPDATE sar.clientes_novos
SET id_corrent_erp = v_id_corrent,
sincronizado = TRUE,
erro_sync = NULL
WHERE id = NEW.id;
EXCEPTION WHEN OTHERS THEN
-- Registra o erro sem abortar — rep vê o status na UI
UPDATE sar.clientes_novos
SET sincronizado = FALSE,
erro_sync = SQLERRM
WHERE id = NEW.id;
END;
RETURN NEW;
END;
$$;
-- Criar (ou recriar) o trigger
DROP TRIGGER IF EXISTS trg_cliente_novo ON sar.clientes_novos;
CREATE TRIGGER trg_cliente_novo
AFTER INSERT ON sar.clientes_novos
FOR EACH ROW
EXECUTE FUNCTION sar.fn_sync_cliente_to_erp();
-- ---------------------------------------------------------------------------
-- Verificação final
-- ---------------------------------------------------------------------------
SELECT
trigger_name,
event_object_schema || '.' || event_object_table AS tabela,
event_manipulation AS evento,
action_timing AS timing
FROM information_schema.triggers
WHERE trigger_schema = 'sar'
ORDER BY tabela, trigger_name;