first commit
This commit is contained in:
@@ -0,0 +1,451 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "tenant_status" AS ENUM ('trial', 'ativo', 'inadimplente', 'suspenso', 'cancelado');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "cobranca_status" AS ENUM ('pendente', 'pago', 'atrasado', 'cancelado');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "user_role" AS ENUM ('tenant_admin', 'vendedor', 'financeiro', 'visualizador');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "item_tipo" AS ENUM ('produto', 'servico');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "venda_status" AS ENUM ('orcamento', 'confirmada', 'cancelada', 'concluida');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "parcela_status" AS ENUM ('pendente', 'pago', 'atrasado', 'cancelado');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "master_admins" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"nome" VARCHAR(150) NOT NULL,
|
||||
"email" VARCHAR(150) NOT NULL,
|
||||
"senha_hash" TEXT NOT NULL,
|
||||
"ativo" BOOLEAN NOT NULL DEFAULT true,
|
||||
"mfa_secret" TEXT,
|
||||
"ultimo_login" TIMESTAMPTZ(6),
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"atualizado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "master_admins_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "evolution_api_config" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"nome" VARCHAR(100) NOT NULL,
|
||||
"base_url" VARCHAR(255) NOT NULL,
|
||||
"api_key_cripto" TEXT NOT NULL,
|
||||
"versao" VARCHAR(20) NOT NULL DEFAULT '2.3',
|
||||
"ativo" BOOLEAN NOT NULL DEFAULT true,
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "evolution_api_config_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "master_email_config" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"smtp_host" VARCHAR(150) NOT NULL,
|
||||
"smtp_porta" INTEGER NOT NULL,
|
||||
"smtp_usuario" VARCHAR(150) NOT NULL,
|
||||
"smtp_senha_cripto" TEXT NOT NULL,
|
||||
"email_remetente" VARCHAR(150) NOT NULL,
|
||||
"ativo" BOOLEAN NOT NULL DEFAULT true,
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "master_email_config_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "planos" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"nome" VARCHAR(100) NOT NULL,
|
||||
"descricao" TEXT,
|
||||
"valor_mensal" DECIMAL(10,2) NOT NULL,
|
||||
"limite_customers" INTEGER,
|
||||
"limite_usuarios" INTEGER,
|
||||
"limite_instancias_whatsapp" INTEGER NOT NULL DEFAULT 1,
|
||||
"ativo" BOOLEAN NOT NULL DEFAULT true,
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "planos_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "tenants" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"razao_social" VARCHAR(200) NOT NULL,
|
||||
"nome_fantasia" VARCHAR(200),
|
||||
"documento" VARCHAR(20),
|
||||
"email_contato" VARCHAR(150) NOT NULL,
|
||||
"telefone" VARCHAR(20),
|
||||
"logo_url" TEXT,
|
||||
"cor_primaria" VARCHAR(7),
|
||||
"plano_id" UUID,
|
||||
"status" "tenant_status" NOT NULL DEFAULT 'trial',
|
||||
"dia_vencimento_mensalidade" SMALLINT NOT NULL DEFAULT 10,
|
||||
"trial_expira_em" TIMESTAMPTZ(6),
|
||||
"criado_por" UUID,
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"atualizado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "tenants_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "tenant_faturas" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"tenant_id" UUID NOT NULL,
|
||||
"referencia" VARCHAR(7) NOT NULL,
|
||||
"valor" DECIMAL(10,2) NOT NULL,
|
||||
"vencimento" DATE NOT NULL,
|
||||
"pago_em" TIMESTAMPTZ(6),
|
||||
"status" "cobranca_status" NOT NULL DEFAULT 'pendente',
|
||||
"alerta_enviado_em" TIMESTAMPTZ(6),
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "tenant_faturas_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "tenant_users" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"tenant_id" UUID NOT NULL,
|
||||
"nome" VARCHAR(150) NOT NULL,
|
||||
"email" VARCHAR(150) NOT NULL,
|
||||
"senha_hash" TEXT NOT NULL,
|
||||
"role" "user_role" NOT NULL DEFAULT 'vendedor',
|
||||
"ativo" BOOLEAN NOT NULL DEFAULT true,
|
||||
"mfa_secret" TEXT,
|
||||
"ultimo_login" TIMESTAMPTZ(6),
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "tenant_users_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "auth_refresh_tokens" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"usuario_tipo" VARCHAR(20) NOT NULL,
|
||||
"usuario_id" UUID NOT NULL,
|
||||
"token_hash" TEXT NOT NULL,
|
||||
"expira_em" TIMESTAMPTZ(6) NOT NULL,
|
||||
"revogado" BOOLEAN NOT NULL DEFAULT false,
|
||||
"ip_origem" INET,
|
||||
"user_agent" TEXT,
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "auth_refresh_tokens_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "password_reset_tokens" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"usuario_tipo" VARCHAR(20) NOT NULL,
|
||||
"usuario_id" UUID NOT NULL,
|
||||
"token_hash" TEXT NOT NULL,
|
||||
"expira_em" TIMESTAMPTZ(6) NOT NULL,
|
||||
"usado" BOOLEAN NOT NULL DEFAULT false,
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "password_reset_tokens_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "login_attempts" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"email" VARCHAR(150) NOT NULL,
|
||||
"usuario_tipo" VARCHAR(20) NOT NULL,
|
||||
"sucesso" BOOLEAN NOT NULL,
|
||||
"ip_origem" INET,
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "login_attempts_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "tenant_whatsapp_instances" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"tenant_id" UUID NOT NULL,
|
||||
"evolution_config_id" UUID,
|
||||
"instance_name" VARCHAR(100) NOT NULL,
|
||||
"numero_conectado" VARCHAR(20),
|
||||
"status" VARCHAR(30) NOT NULL DEFAULT 'desconectado',
|
||||
"qr_code_gerado_em" TIMESTAMPTZ(6),
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "tenant_whatsapp_instances_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "tenant_email_config" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"tenant_id" UUID NOT NULL,
|
||||
"smtp_host" VARCHAR(150),
|
||||
"smtp_porta" INTEGER,
|
||||
"smtp_usuario" VARCHAR(150),
|
||||
"smtp_senha_cripto" TEXT,
|
||||
"email_remetente" VARCHAR(150),
|
||||
"usar_config_padrao" BOOLEAN NOT NULL DEFAULT true,
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "tenant_email_config_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "alert_templates" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"tenant_id" UUID,
|
||||
"tipo" VARCHAR(50) NOT NULL,
|
||||
"canal" VARCHAR(20) NOT NULL,
|
||||
"assunto" VARCHAR(200),
|
||||
"corpo" TEXT NOT NULL,
|
||||
"ativo" BOOLEAN NOT NULL DEFAULT true,
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "alert_templates_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "alert_logs" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"tenant_id" UUID,
|
||||
"destinatario_tipo" VARCHAR(20) NOT NULL,
|
||||
"destinatario_id" UUID NOT NULL,
|
||||
"canal" VARCHAR(20) NOT NULL,
|
||||
"template_id" UUID,
|
||||
"conteudo_enviado" TEXT,
|
||||
"status" VARCHAR(20) NOT NULL DEFAULT 'enviado',
|
||||
"erro" TEXT,
|
||||
"enviado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "alert_logs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "customers" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"tenant_id" UUID NOT NULL,
|
||||
"razao_social" VARCHAR(200) NOT NULL,
|
||||
"nome_fantasia" VARCHAR(200),
|
||||
"documento" VARCHAR(20),
|
||||
"email" VARCHAR(150),
|
||||
"telefone_whatsapp" VARCHAR(20),
|
||||
"endereco" TEXT,
|
||||
"cidade" VARCHAR(100),
|
||||
"uf" CHAR(2),
|
||||
"observacoes" TEXT,
|
||||
"ativo" BOOLEAN NOT NULL DEFAULT true,
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"atualizado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "customers_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "customer_contacts" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"customer_id" UUID NOT NULL,
|
||||
"nome" VARCHAR(150) NOT NULL,
|
||||
"cargo" VARCHAR(100),
|
||||
"email" VARCHAR(150),
|
||||
"telefone_whatsapp" VARCHAR(20),
|
||||
"principal" BOOLEAN NOT NULL DEFAULT false,
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "customer_contacts_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "products_services" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"tenant_id" UUID NOT NULL,
|
||||
"tipo" "item_tipo" NOT NULL,
|
||||
"nome" VARCHAR(200) NOT NULL,
|
||||
"descricao" TEXT,
|
||||
"sku" VARCHAR(50),
|
||||
"valor_venda" DECIMAL(12,2) NOT NULL,
|
||||
"valor_custo" DECIMAL(12,2),
|
||||
"unidade" VARCHAR(20) NOT NULL DEFAULT 'un',
|
||||
"recorrente" BOOLEAN NOT NULL DEFAULT false,
|
||||
"ativo" BOOLEAN NOT NULL DEFAULT true,
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"atualizado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "products_services_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "sales" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"tenant_id" UUID NOT NULL,
|
||||
"customer_id" UUID NOT NULL,
|
||||
"vendedor_id" UUID,
|
||||
"numero_venda" SERIAL NOT NULL,
|
||||
"status" "venda_status" NOT NULL DEFAULT 'confirmada',
|
||||
"valor_total" DECIMAL(12,2) NOT NULL DEFAULT 0,
|
||||
"desconto" DECIMAL(12,2) NOT NULL DEFAULT 0,
|
||||
"observacoes" TEXT,
|
||||
"data_venda" DATE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"atualizado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "sales_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "sale_items" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"sale_id" UUID NOT NULL,
|
||||
"product_service_id" UUID NOT NULL,
|
||||
"quantidade" DECIMAL(10,2) NOT NULL DEFAULT 1,
|
||||
"valor_unitario" DECIMAL(12,2) NOT NULL,
|
||||
"valor_total" DECIMAL(12,2) NOT NULL,
|
||||
|
||||
CONSTRAINT "sale_items_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "sale_payments" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"sale_id" UUID NOT NULL,
|
||||
"numero_parcela" SMALLINT NOT NULL DEFAULT 1,
|
||||
"valor" DECIMAL(12,2) NOT NULL,
|
||||
"vencimento" DATE NOT NULL,
|
||||
"pago_em" TIMESTAMPTZ(6),
|
||||
"forma_pagamento" VARCHAR(30),
|
||||
"status" "parcela_status" NOT NULL DEFAULT 'pendente',
|
||||
"alerta_enviado_em" TIMESTAMPTZ(6),
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "sale_payments_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "audit_logs" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"tenant_id" UUID,
|
||||
"usuario_tipo" VARCHAR(20) NOT NULL,
|
||||
"usuario_id" UUID,
|
||||
"acao" VARCHAR(100) NOT NULL,
|
||||
"entidade" VARCHAR(50),
|
||||
"entidade_id" UUID,
|
||||
"detalhes" JSONB,
|
||||
"ip_origem" INET,
|
||||
"criado_em" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "audit_logs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "master_admins_email_key" ON "master_admins"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_tenants_status" ON "tenants"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "tenant_faturas_tenant_id_referencia_key" ON "tenant_faturas"("tenant_id", "referencia");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_tenant_users_tenant" ON "tenant_users"("tenant_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "tenant_users_tenant_id_email_key" ON "tenant_users"("tenant_id", "email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_refresh_tokens_usuario" ON "auth_refresh_tokens"("usuario_tipo", "usuario_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_login_attempts_email_data" ON "login_attempts"("email", "criado_em");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "tenant_whatsapp_instances_instance_name_key" ON "tenant_whatsapp_instances"("instance_name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "tenant_email_config_tenant_id_key" ON "tenant_email_config"("tenant_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_alert_logs_tenant" ON "alert_logs"("tenant_id", "enviado_em");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_customers_tenant" ON "customers"("tenant_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_products_tenant" ON "products_services"("tenant_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "products_services_tenant_id_sku_key" ON "products_services"("tenant_id", "sku");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_sales_tenant_data" ON "sales"("tenant_id", "data_venda");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_sales_customer" ON "sales"("customer_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_sale_items_sale" ON "sale_items"("sale_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_audit_tenant_data" ON "audit_logs"("tenant_id", "criado_em");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "tenants" ADD CONSTRAINT "tenants_plano_id_fkey" FOREIGN KEY ("plano_id") REFERENCES "planos"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "tenants" ADD CONSTRAINT "tenants_criado_por_fkey" FOREIGN KEY ("criado_por") REFERENCES "master_admins"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "tenant_faturas" ADD CONSTRAINT "tenant_faturas_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "tenant_users" ADD CONSTRAINT "tenant_users_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "tenant_whatsapp_instances" ADD CONSTRAINT "tenant_whatsapp_instances_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "tenant_whatsapp_instances" ADD CONSTRAINT "tenant_whatsapp_instances_evolution_config_id_fkey" FOREIGN KEY ("evolution_config_id") REFERENCES "evolution_api_config"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "tenant_email_config" ADD CONSTRAINT "tenant_email_config_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "alert_templates" ADD CONSTRAINT "alert_templates_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "alert_logs" ADD CONSTRAINT "alert_logs_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "alert_logs" ADD CONSTRAINT "alert_logs_template_id_fkey" FOREIGN KEY ("template_id") REFERENCES "alert_templates"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "customers" ADD CONSTRAINT "customers_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "customer_contacts" ADD CONSTRAINT "customer_contacts_customer_id_fkey" FOREIGN KEY ("customer_id") REFERENCES "customers"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "products_services" ADD CONSTRAINT "products_services_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "sales" ADD CONSTRAINT "sales_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "sales" ADD CONSTRAINT "sales_customer_id_fkey" FOREIGN KEY ("customer_id") REFERENCES "customers"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "sales" ADD CONSTRAINT "sales_vendedor_id_fkey" FOREIGN KEY ("vendedor_id") REFERENCES "tenant_users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "sale_items" ADD CONSTRAINT "sale_items_sale_id_fkey" FOREIGN KEY ("sale_id") REFERENCES "sales"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "sale_items" ADD CONSTRAINT "sale_items_product_service_id_fkey" FOREIGN KEY ("product_service_id") REFERENCES "products_services"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "sale_payments" ADD CONSTRAINT "sale_payments_sale_id_fkey" FOREIGN KEY ("sale_id") REFERENCES "sales"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "audit_logs" ADD CONSTRAINT "audit_logs_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,119 @@
|
||||
-- Migration manual: construtos que o Prisma DSL não expressa nativamente.
|
||||
-- Fonte de verdade: Etapas/seconectai-vendas-schema.sql
|
||||
-- Contém: extensão pgcrypto, CHECK constraint, índices parciais, views de
|
||||
-- dashboard e Row Level Security (ENABLE + CREATE POLICY) para isolamento
|
||||
-- entre tenants.
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
|
||||
-- ============================================================
|
||||
-- CHECK constraints
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE "tenants"
|
||||
ADD CONSTRAINT "tenants_dia_vencimento_mensalidade_check"
|
||||
CHECK ("dia_vencimento_mensalidade" BETWEEN 1 AND 28);
|
||||
|
||||
-- ============================================================
|
||||
-- Índices parciais
|
||||
-- ============================================================
|
||||
|
||||
CREATE INDEX "idx_tenant_faturas_venc"
|
||||
ON "tenant_faturas"("vencimento")
|
||||
WHERE "status" IN ('pendente', 'atrasado');
|
||||
|
||||
CREATE INDEX "idx_sale_payments_venc"
|
||||
ON "sale_payments"("vencimento")
|
||||
WHERE "status" IN ('pendente', 'atrasado');
|
||||
|
||||
-- ============================================================
|
||||
-- Views de dashboard (painel do tenant)
|
||||
-- ============================================================
|
||||
|
||||
CREATE VIEW "vw_resumo_vendas_semanal" AS
|
||||
SELECT
|
||||
s.tenant_id,
|
||||
date_trunc('week', s.data_venda)::date AS semana,
|
||||
COUNT(*) AS qtd_vendas,
|
||||
SUM(s.valor_total) AS total_vendido
|
||||
FROM sales s
|
||||
WHERE s.status IN ('confirmada', 'concluida')
|
||||
GROUP BY s.tenant_id, date_trunc('week', s.data_venda);
|
||||
|
||||
CREATE VIEW "vw_resumo_vendas_mensal" AS
|
||||
SELECT
|
||||
s.tenant_id,
|
||||
date_trunc('month', s.data_venda)::date AS mes,
|
||||
COUNT(*) AS qtd_vendas,
|
||||
SUM(s.valor_total) AS total_vendido
|
||||
FROM sales s
|
||||
WHERE s.status IN ('confirmada', 'concluida')
|
||||
GROUP BY s.tenant_id, date_trunc('month', s.data_venda);
|
||||
|
||||
CREATE VIEW "vw_recebimentos" AS
|
||||
SELECT
|
||||
sp.id,
|
||||
s.tenant_id,
|
||||
s.customer_id,
|
||||
sp.valor,
|
||||
sp.vencimento,
|
||||
sp.status,
|
||||
sp.pago_em
|
||||
FROM sale_payments sp
|
||||
JOIN sales s ON s.id = sp.sale_id;
|
||||
|
||||
-- ============================================================
|
||||
-- Row Level Security — isolamento entre tenants
|
||||
-- ============================================================
|
||||
-- A aplicação deve definir, por conexão/transação:
|
||||
-- SET app.current_tenant_id = '<uuid-do-tenant-logado>';
|
||||
-- Usuários master (que enxergam todos os tenants) usam uma role de banco
|
||||
-- separada (ex: app_master) que faz BYPASS RLS, ou uma policy adicional
|
||||
-- que libera quando app.is_master = 'true'. Implementado na Etapa 2.
|
||||
|
||||
ALTER TABLE "customers" ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "customer_contacts" ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "products_services" ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "sales" ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "sale_items" ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "sale_payments" ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "tenant_users" ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "tenant_whatsapp_instances" ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "tenant_email_config" ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY "tenant_isolation_customers" ON "customers"
|
||||
USING ("tenant_id" = current_setting('app.current_tenant_id', true)::uuid);
|
||||
|
||||
CREATE POLICY "tenant_isolation_products" ON "products_services"
|
||||
USING ("tenant_id" = current_setting('app.current_tenant_id', true)::uuid);
|
||||
|
||||
CREATE POLICY "tenant_isolation_sales" ON "sales"
|
||||
USING ("tenant_id" = current_setting('app.current_tenant_id', true)::uuid);
|
||||
|
||||
CREATE POLICY "tenant_isolation_tenant_users" ON "tenant_users"
|
||||
USING ("tenant_id" = current_setting('app.current_tenant_id', true)::uuid);
|
||||
|
||||
CREATE POLICY "tenant_isolation_whatsapp" ON "tenant_whatsapp_instances"
|
||||
USING ("tenant_id" = current_setting('app.current_tenant_id', true)::uuid);
|
||||
|
||||
CREATE POLICY "tenant_isolation_email_config" ON "tenant_email_config"
|
||||
USING ("tenant_id" = current_setting('app.current_tenant_id', true)::uuid);
|
||||
|
||||
-- sale_items e customer_contacts não têm tenant_id direto: isolar via join
|
||||
CREATE POLICY "tenant_isolation_sale_items" ON "sale_items"
|
||||
USING ("sale_id" IN (
|
||||
SELECT id FROM sales
|
||||
WHERE tenant_id = current_setting('app.current_tenant_id', true)::uuid
|
||||
));
|
||||
|
||||
CREATE POLICY "tenant_isolation_sale_payments" ON "sale_payments"
|
||||
USING ("sale_id" IN (
|
||||
SELECT id FROM sales
|
||||
WHERE tenant_id = current_setting('app.current_tenant_id', true)::uuid
|
||||
));
|
||||
|
||||
CREATE POLICY "tenant_isolation_customer_contacts" ON "customer_contacts"
|
||||
USING ("customer_id" IN (
|
||||
SELECT id FROM customers
|
||||
WHERE tenant_id = current_setting('app.current_tenant_id', true)::uuid
|
||||
));
|
||||
@@ -0,0 +1,53 @@
|
||||
-- Migration manual: roles de aplicação usadas pela Etapa 2 (Autenticação/RLS).
|
||||
--
|
||||
-- Por que duas roles novas em vez de reaproveitar a role do DATABASE_URL
|
||||
-- (`seconectai`, dono das tabelas): a role criada pelo `POSTGRES_USER` do
|
||||
-- docker-compose é a role bootstrap do initdb, portanto é SUPERUSER — e
|
||||
-- superusers do Postgres **sempre** ignoram Row Level Security, mesmo com
|
||||
-- `ENABLE ROW LEVEL SECURITY`/`FORCE ROW LEVEL SECURITY` nas tabelas. Se a
|
||||
-- aplicação continuasse consultando o banco com essa role em runtime, o
|
||||
-- isolamento por tenant seria só decorativo. Por isso:
|
||||
--
|
||||
-- * `app_master` — LOGIN + BYPASSRLS, mas SEM ser superuser. Usada via
|
||||
-- `DATABASE_URL_MASTER` pelo painel master (enxerga todos os tenants),
|
||||
-- como pedido no prompt da etapa.
|
||||
-- * `app_tenant` — LOGIN comum, SEM BYPASSRLS e SEM ser superuser. Usada
|
||||
-- via `DATABASE_URL_TENANT` para toda query autenticada como
|
||||
-- tenant_user, dentro da transação que executa
|
||||
-- `SET LOCAL app.current_tenant_id`; é essa role que faz as policies de
|
||||
-- RLS (`prisma/migrations/20260812161100_rls_views_constraints`) valerem
|
||||
-- de fato. (Desvio registrado em Etapas/09-controle-de-progresso.md: o
|
||||
-- prompt original só citava `DATABASE_URL_MASTER`.)
|
||||
--
|
||||
-- `DATABASE_URL` (role `seconectai`) continua sendo usada só para
|
||||
-- migrations e para o `PrismaService` "admin" (health check, bootstrap de
|
||||
-- login antes de existir um tenant_id de sessão — ver AuthService).
|
||||
--
|
||||
-- As senhas abaixo são placeholders de desenvolvimento, no mesmo espírito
|
||||
-- de `POSTGRES_PASSWORD`/`JWT_SECRET` em `.env.example`: troque-as em
|
||||
-- produção com `ALTER ROLE ... WITH PASSWORD '...'` e atualize
|
||||
-- `DATABASE_URL_MASTER`/`DATABASE_URL_TENANT` de acordo.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'app_master') THEN
|
||||
CREATE ROLE app_master WITH LOGIN PASSWORD 'app_master_changeme' NOSUPERUSER BYPASSRLS;
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'app_tenant') THEN
|
||||
CREATE ROLE app_tenant WITH LOGIN PASSWORD 'app_tenant_changeme' NOSUPERUSER NOBYPASSRLS;
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
GRANT USAGE ON SCHEMA public TO app_master, app_tenant;
|
||||
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_master, app_tenant;
|
||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_master, app_tenant;
|
||||
|
||||
-- Cobre tabelas/sequences criadas por migrations futuras (etapas 3+),
|
||||
-- desde que aplicadas com a mesma role que rodou esta migration.
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_master, app_tenant;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||||
GRANT USAGE, SELECT ON SEQUENCES TO app_master, app_tenant;
|
||||
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
||||
@@ -0,0 +1,438 @@
|
||||
// SeConectai Vendas — Prisma schema
|
||||
// Traduzido 1:1 a partir de `Etapas/seconectai-vendas-schema.sql` (fonte de verdade).
|
||||
// Constructos que o Prisma DSL não expressa nativamente (views, índices parciais,
|
||||
// CHECK constraints e as policies de RLS) são criados por uma migration manual
|
||||
// em `prisma/migrations/<timestamp>_rls_views_constraints/migration.sql`.
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// ENUMS
|
||||
// ============================================================
|
||||
|
||||
enum tenant_status {
|
||||
trial
|
||||
ativo
|
||||
inadimplente
|
||||
suspenso
|
||||
cancelado
|
||||
}
|
||||
|
||||
enum cobranca_status {
|
||||
pendente
|
||||
pago
|
||||
atrasado
|
||||
cancelado
|
||||
}
|
||||
|
||||
enum user_role {
|
||||
tenant_admin
|
||||
vendedor
|
||||
financeiro
|
||||
visualizador
|
||||
}
|
||||
|
||||
enum item_tipo {
|
||||
produto
|
||||
servico
|
||||
}
|
||||
|
||||
enum venda_status {
|
||||
orcamento
|
||||
confirmada
|
||||
cancelada
|
||||
concluida
|
||||
}
|
||||
|
||||
enum parcela_status {
|
||||
pendente
|
||||
pago
|
||||
atrasado
|
||||
cancelado
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 1. NÍVEL MASTER
|
||||
// ============================================================
|
||||
|
||||
model master_admins {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
nome String @db.VarChar(150)
|
||||
email String @unique @db.VarChar(150)
|
||||
senha_hash String
|
||||
ativo Boolean @default(true)
|
||||
mfa_secret String?
|
||||
ultimo_login DateTime? @db.Timestamptz(6)
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
atualizado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
tenants_criados tenants[]
|
||||
}
|
||||
|
||||
model evolution_api_config {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
nome String @db.VarChar(100)
|
||||
base_url String @db.VarChar(255)
|
||||
api_key_cripto String
|
||||
versao String @default("2.3") @db.VarChar(20)
|
||||
ativo Boolean @default(true)
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
instancias tenant_whatsapp_instances[]
|
||||
}
|
||||
|
||||
model master_email_config {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
smtp_host String @db.VarChar(150)
|
||||
smtp_porta Int
|
||||
smtp_usuario String @db.VarChar(150)
|
||||
smtp_senha_cripto String
|
||||
email_remetente String @db.VarChar(150)
|
||||
ativo Boolean @default(true)
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
}
|
||||
|
||||
model planos {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
nome String @db.VarChar(100)
|
||||
descricao String?
|
||||
valor_mensal Decimal @db.Decimal(10, 2)
|
||||
limite_customers Int?
|
||||
limite_usuarios Int?
|
||||
limite_instancias_whatsapp Int @default(1)
|
||||
ativo Boolean @default(true)
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
tenants tenants[]
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 2. TENANTS
|
||||
// ============================================================
|
||||
|
||||
model tenants {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
razao_social String @db.VarChar(200)
|
||||
nome_fantasia String? @db.VarChar(200)
|
||||
documento String? @db.VarChar(20)
|
||||
email_contato String @db.VarChar(150)
|
||||
telefone String? @db.VarChar(20)
|
||||
logo_url String?
|
||||
cor_primaria String? @db.VarChar(7)
|
||||
plano_id String? @db.Uuid
|
||||
status tenant_status @default(trial)
|
||||
dia_vencimento_mensalidade Int @default(10) @db.SmallInt
|
||||
trial_expira_em DateTime? @db.Timestamptz(6)
|
||||
criado_por String? @db.Uuid
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
atualizado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
plano planos? @relation(fields: [plano_id], references: [id])
|
||||
criador master_admins? @relation(fields: [criado_por], references: [id])
|
||||
|
||||
faturas tenant_faturas[]
|
||||
usuarios tenant_users[]
|
||||
whatsapp_instances tenant_whatsapp_instances[]
|
||||
email_config tenant_email_config?
|
||||
alert_templates alert_templates[]
|
||||
alert_logs alert_logs[]
|
||||
customers customers[]
|
||||
produtos products_services[]
|
||||
vendas sales[]
|
||||
audit_logs audit_logs[]
|
||||
|
||||
@@index([status], map: "idx_tenants_status")
|
||||
}
|
||||
|
||||
model tenant_faturas {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
tenant_id String @db.Uuid
|
||||
referencia String @db.VarChar(7)
|
||||
valor Decimal @db.Decimal(10, 2)
|
||||
vencimento DateTime @db.Date
|
||||
pago_em DateTime? @db.Timestamptz(6)
|
||||
status cobranca_status @default(pendente)
|
||||
alerta_enviado_em DateTime? @db.Timestamptz(6)
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
tenant tenants @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([tenant_id, referencia])
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 3. USUÁRIOS E AUTENTICAÇÃO
|
||||
// ============================================================
|
||||
|
||||
model tenant_users {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
tenant_id String @db.Uuid
|
||||
nome String @db.VarChar(150)
|
||||
email String @db.VarChar(150)
|
||||
senha_hash String
|
||||
role user_role @default(vendedor)
|
||||
ativo Boolean @default(true)
|
||||
mfa_secret String?
|
||||
ultimo_login DateTime? @db.Timestamptz(6)
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
tenant tenants @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
|
||||
|
||||
vendas sales[]
|
||||
|
||||
@@unique([tenant_id, email])
|
||||
@@index([tenant_id], map: "idx_tenant_users_tenant")
|
||||
}
|
||||
|
||||
model auth_refresh_tokens {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
usuario_tipo String @db.VarChar(20)
|
||||
usuario_id String @db.Uuid
|
||||
token_hash String
|
||||
expira_em DateTime @db.Timestamptz(6)
|
||||
revogado Boolean @default(false)
|
||||
ip_origem String? @db.Inet
|
||||
user_agent String?
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
@@index([usuario_tipo, usuario_id], map: "idx_refresh_tokens_usuario")
|
||||
}
|
||||
|
||||
model password_reset_tokens {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
usuario_tipo String @db.VarChar(20)
|
||||
usuario_id String @db.Uuid
|
||||
token_hash String
|
||||
expira_em DateTime @db.Timestamptz(6)
|
||||
usado Boolean @default(false)
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
}
|
||||
|
||||
model login_attempts {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
email String @db.VarChar(150)
|
||||
usuario_tipo String @db.VarChar(20)
|
||||
sucesso Boolean
|
||||
ip_origem String? @db.Inet
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
@@index([email, criado_em], map: "idx_login_attempts_email_data")
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 4. INTEGRAÇÕES POR TENANT
|
||||
// ============================================================
|
||||
|
||||
model tenant_whatsapp_instances {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
tenant_id String @db.Uuid
|
||||
evolution_config_id String? @db.Uuid
|
||||
instance_name String @unique @db.VarChar(100)
|
||||
numero_conectado String? @db.VarChar(20)
|
||||
status String @default("desconectado") @db.VarChar(30)
|
||||
qr_code_gerado_em DateTime? @db.Timestamptz(6)
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
tenant tenants @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
|
||||
evolution_config evolution_api_config? @relation(fields: [evolution_config_id], references: [id])
|
||||
}
|
||||
|
||||
model tenant_email_config {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
tenant_id String @unique @db.Uuid
|
||||
smtp_host String? @db.VarChar(150)
|
||||
smtp_porta Int?
|
||||
smtp_usuario String? @db.VarChar(150)
|
||||
smtp_senha_cripto String?
|
||||
email_remetente String? @db.VarChar(150)
|
||||
usar_config_padrao Boolean @default(true)
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
tenant tenants @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model alert_templates {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
tenant_id String? @db.Uuid
|
||||
tipo String @db.VarChar(50)
|
||||
canal String @db.VarChar(20)
|
||||
assunto String? @db.VarChar(200)
|
||||
corpo String
|
||||
ativo Boolean @default(true)
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
tenant tenants? @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
|
||||
|
||||
alert_logs alert_logs[]
|
||||
}
|
||||
|
||||
model alert_logs {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
tenant_id String? @db.Uuid
|
||||
destinatario_tipo String @db.VarChar(20)
|
||||
destinatario_id String @db.Uuid
|
||||
canal String @db.VarChar(20)
|
||||
template_id String? @db.Uuid
|
||||
conteudo_enviado String?
|
||||
status String @default("enviado") @db.VarChar(20)
|
||||
erro String?
|
||||
enviado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
tenant tenants? @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
|
||||
template alert_templates? @relation(fields: [template_id], references: [id])
|
||||
|
||||
@@index([tenant_id, enviado_em], map: "idx_alert_logs_tenant")
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 5. CLIENTES DO TENANT ("customers")
|
||||
// ============================================================
|
||||
|
||||
model customers {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
tenant_id String @db.Uuid
|
||||
razao_social String @db.VarChar(200)
|
||||
nome_fantasia String? @db.VarChar(200)
|
||||
documento String? @db.VarChar(20)
|
||||
email String? @db.VarChar(150)
|
||||
telefone_whatsapp String? @db.VarChar(20)
|
||||
endereco String?
|
||||
cidade String? @db.VarChar(100)
|
||||
uf String? @db.Char(2)
|
||||
observacoes String?
|
||||
ativo Boolean @default(true)
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
atualizado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
tenant tenants @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
|
||||
contatos customer_contacts[]
|
||||
vendas sales[]
|
||||
|
||||
@@index([tenant_id], map: "idx_customers_tenant")
|
||||
}
|
||||
|
||||
model customer_contacts {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
customer_id String @db.Uuid
|
||||
nome String @db.VarChar(150)
|
||||
cargo String? @db.VarChar(100)
|
||||
email String? @db.VarChar(150)
|
||||
telefone_whatsapp String? @db.VarChar(20)
|
||||
principal Boolean @default(false)
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
customer customers @relation(fields: [customer_id], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 6. PRODUTOS E SERVIÇOS DO TENANT
|
||||
// ============================================================
|
||||
|
||||
model products_services {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
tenant_id String @db.Uuid
|
||||
tipo item_tipo
|
||||
nome String @db.VarChar(200)
|
||||
descricao String?
|
||||
sku String? @db.VarChar(50)
|
||||
valor_venda Decimal @db.Decimal(12, 2)
|
||||
valor_custo Decimal? @db.Decimal(12, 2)
|
||||
unidade String @default("un") @db.VarChar(20)
|
||||
recorrente Boolean @default(false)
|
||||
ativo Boolean @default(true)
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
atualizado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
tenant tenants @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
|
||||
|
||||
itens_venda sale_items[]
|
||||
|
||||
@@unique([tenant_id, sku])
|
||||
@@index([tenant_id], map: "idx_products_tenant")
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 7. VENDAS
|
||||
// ============================================================
|
||||
|
||||
model sales {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
tenant_id String @db.Uuid
|
||||
customer_id String @db.Uuid
|
||||
vendedor_id String? @db.Uuid
|
||||
numero_venda Int @default(autoincrement())
|
||||
status venda_status @default(confirmada)
|
||||
valor_total Decimal @default(0) @db.Decimal(12, 2)
|
||||
desconto Decimal @default(0) @db.Decimal(12, 2)
|
||||
observacoes String?
|
||||
data_venda DateTime @default(now()) @db.Date
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
atualizado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
tenant tenants @relation(fields: [tenant_id], references: [id], onDelete: Cascade)
|
||||
customer customers @relation(fields: [customer_id], references: [id])
|
||||
vendedor tenant_users? @relation(fields: [vendedor_id], references: [id])
|
||||
|
||||
itens sale_items[]
|
||||
parcelas sale_payments[]
|
||||
|
||||
@@index([tenant_id, data_venda], map: "idx_sales_tenant_data")
|
||||
@@index([customer_id], map: "idx_sales_customer")
|
||||
}
|
||||
|
||||
model sale_items {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
sale_id String @db.Uuid
|
||||
product_service_id String @db.Uuid
|
||||
quantidade Decimal @default(1) @db.Decimal(10, 2)
|
||||
valor_unitario Decimal @db.Decimal(12, 2)
|
||||
valor_total Decimal @db.Decimal(12, 2)
|
||||
|
||||
sale sales @relation(fields: [sale_id], references: [id], onDelete: Cascade)
|
||||
produto products_services @relation(fields: [product_service_id], references: [id])
|
||||
|
||||
@@index([sale_id], map: "idx_sale_items_sale")
|
||||
}
|
||||
|
||||
model sale_payments {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
sale_id String @db.Uuid
|
||||
numero_parcela Int @default(1) @db.SmallInt
|
||||
valor Decimal @db.Decimal(12, 2)
|
||||
vencimento DateTime @db.Date
|
||||
pago_em DateTime? @db.Timestamptz(6)
|
||||
forma_pagamento String? @db.VarChar(30)
|
||||
status parcela_status @default(pendente)
|
||||
alerta_enviado_em DateTime? @db.Timestamptz(6)
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
sale sales @relation(fields: [sale_id], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 8. AUDITORIA GERAL
|
||||
// ============================================================
|
||||
|
||||
model audit_logs {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
tenant_id String? @db.Uuid
|
||||
usuario_tipo String @db.VarChar(20)
|
||||
usuario_id String? @db.Uuid
|
||||
acao String @db.VarChar(100)
|
||||
entidade String? @db.VarChar(50)
|
||||
entidade_id String? @db.Uuid
|
||||
detalhes Json?
|
||||
ip_origem String? @db.Inet
|
||||
criado_em DateTime @default(now()) @db.Timestamptz(6)
|
||||
|
||||
tenant tenants? @relation(fields: [tenant_id], references: [id], onDelete: SetNull)
|
||||
|
||||
@@index([tenant_id, criado_em], map: "idx_audit_tenant_data")
|
||||
}
|
||||
Reference in New Issue
Block a user