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:
@@ -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");
|
|
||||||
@@ -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;
|
|
||||||
@@ -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");
|
|
||||||
@@ -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");
|
|
||||||
@@ -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");
|
|
||||||
@@ -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 { PlusOutlined } from '@ant-design/icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import {
|
import { faBell, faBars, faBuilding, faRightFromBracket } from '@fortawesome/free-solid-svg-icons';
|
||||||
faBell,
|
|
||||||
faMagnifyingGlass,
|
|
||||||
faBars,
|
|
||||||
faRightFromBracket,
|
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import { useNavigate } from '@tanstack/react-router';
|
import { useNavigate } from '@tanstack/react-router';
|
||||||
import { brandTokens } from '../../lib/theme';
|
import { brandTokens } from '../../lib/theme';
|
||||||
import { FoundationStatus } from './FoundationStatus';
|
import { FoundationStatus } from './FoundationStatus';
|
||||||
import { usePendingCount } from '../../lib/queries/notifications';
|
import { usePendingCount } from '../../lib/queries/notifications';
|
||||||
import { useCurrentUser } from '../../lib/queries/auth';
|
import { useCurrentUser } from '../../lib/queries/auth';
|
||||||
|
import { useCompany } from '../../lib/queries/company';
|
||||||
import { authStore } from '../../lib/auth-store';
|
import { authStore } from '../../lib/auth-store';
|
||||||
|
|
||||||
interface TopbarProps {
|
interface TopbarProps {
|
||||||
@@ -33,6 +29,11 @@ export function Topbar({ onToggleSidebar }: TopbarProps) {
|
|||||||
const { data: pendingData } = usePendingCount();
|
const { data: pendingData } = usePendingCount();
|
||||||
const pendingCount = pendingData?.count ?? 0;
|
const pendingCount = pendingData?.count ?? 0;
|
||||||
const { data: user } = useCurrentUser();
|
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
|
const initials = user?.nome
|
||||||
? user.nome
|
? user.nome
|
||||||
.split(' ')
|
.split(' ')
|
||||||
@@ -121,17 +122,39 @@ export function Topbar({ onToggleSidebar }: TopbarProps) {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{/* Centro: search (Supervisor/Admin) */}
|
{/* Centro: empresa ativa */}
|
||||||
<Flex flex={1} justify="center" style={{ maxWidth: 480, margin: '0 var(--space-2xl)' }}>
|
<Flex flex={1} justify="center" style={{ margin: '0 var(--space-2xl)' }}>
|
||||||
<Input
|
<Flex align="center" gap={10}>
|
||||||
size="large"
|
<FontAwesomeIcon
|
||||||
placeholder="Buscar cliente, pedido, produto..."
|
icon={faBuilding}
|
||||||
prefix={
|
style={{ color: 'var(--text-muted)', fontSize: 16, flexShrink: 0 }}
|
||||||
<FontAwesomeIcon icon={faMagnifyingGlass} style={{ color: 'var(--text-muted)' }} />
|
|
||||||
}
|
|
||||||
style={{ borderRadius: 12 }}
|
|
||||||
aria-label="Buscar"
|
|
||||||
/>
|
/>
|
||||||
|
<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>
|
</Flex>
|
||||||
|
|
||||||
{/* Lado direito: novo pedido + status fundação + notificações + perfil */}
|
{/* Lado direito: novo pedido + status fundação + notificações + perfil */}
|
||||||
|
|||||||
455
scripts/sar-triggers.sql
Normal file
455
scripts/sar-triggers.sql
Normal 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;
|
||||||
Reference in New Issue
Block a user