Cadastre serviços com código LC116 para emissão de NFS-e
Serviços prestados como instalação, criação e montagem
💡 Serviços comuns para comunicação visual:7.06 (Propaganda e publicidade) ·
7.09 (Disponibilização de espaço) ·
14.05 (Restauração, recondicionamento) ·
17.09 (Comunicação visual)
Código
Nome / Descrição
Cód. LC116
ISS (%)
Município
Valor Padrão
Status
Ações
Nenhum serviço fiscal cadastrado
ℹ️ Nenhuma nota fiscal emitida ainda.
🖥️
Backend Node.js
Necessário para emissão real de NF (evita bloqueio CORS)
Sem backend configurado — configure acima para habilitar emissao de NF.
🏢
Dados do Emitente
🔐
Certificado Digital A1
🛡️ Como funciona com segurança:
O certificado nunca é armazenado no navegador. Ele é enviado diretamente ao provedor de NF (Focus NF-e, NFe.io, eNotas) via HTTPS. O frontend apenas transmite — não guarda o arquivo.
📄
Nenhum certificado configurado
Configure seu certificado A1 (.pfx ou .p12) para emissão automática de NF
📁
Clique ou arraste o arquivo aqui
Formatos aceitos: .pfx, .p12 · Máx: 500KB
⚠️ A senha nunca é armazenada no banco de dados. É usada apenas para transmitir o certificado ao provedor.
🔗
Provedor de Emissão
📤
Exportar & SQL
🗄️ SQL Supabase (itens_fiscais + certificados)
-- itens_fiscais (já criado anteriormente)
-- Adicionar tabela certificados_fiscais:
CREATE TABLE IF NOT EXISTS certificados_fiscais (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE UNIQUE,
provedor TEXT NOT NULL,
ambiente TEXT DEFAULT 'homologacao',
cert_status TEXT DEFAULT 'nao_configurado',
cert_nome TEXT,
cert_validade DATE,
cnpj TEXT,
razao_social TEXT,
ie TEXT, im TEXT,
regime TEXT DEFAULT '1',
cnae TEXT,
ibge TEXT,
cep TEXT, logradouro TEXT, numero TEXT,
bairro TEXT, cidade TEXT, uf TEXT,
iss_padrao NUMERIC DEFAULT 2,
client_id TEXT,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- RLS: cada empresa só acessa seu próprio certificado
ALTER TABLE certificados_fiscais ENABLE ROW LEVEL SECURITY;
CREATE POLICY "own_cert_fiscal" ON certificados_fiscais FOR ALL
USING (empresa_id = get_empresa_id())
WITH CHECK (empresa_id = get_empresa_id());
GRANT SELECT,INSERT,UPDATE ON certificados_fiscais TO authenticated;
-- NUNCA armazenar o arquivo .pfx ou senha no banco!
-- O certificado é enviado diretamente ao provedor via HTTPS.
-- O banco armazena apenas metadados (nome, validade, status).
🧪 Insumos
Matérias-primas · Custos · Estoque mínimo
Carregando insumos...
📦 Estoque
Movimentações · Saldo · Alertas
Carregando estoque...
🛒 Compras
Pedidos de compra · Recebimentos · Atualização de estoque
Configure os valores padrão usados nos calculadores de produtos complexos
🏗️
Fachada ACM
💡
Iluminação LED
🔡
Letra Caixa
👷
Mão de Obra
🔧
Insumos e Fixadores
Aplicado na área total antes do ACM
Custo unitário de cada fixador
Quantidade de módulos por metro quadrado
Aplicado sobre o material calculado
💡 Esses valores são usados como padrão nos calculadores de Fachada ACM, Letra Caixa, Totem, Letreiro LED, Wrap e Instalação. Você pode ajustar em cada orçamento individualmente.
🤖
Assistente IA — OpenAI
A IA usa o modelo GPT-4o-mini para analisar orçamentos, sugerir preços e calcular metalon.
Sua API Key é salva somente no seu navegador (localStorage) e nunca é enviada ao nosso servidor.
Estes valores são usados como padrão em novos orçamentos. Você pode alterar por orçamento.
📈
Escala de Produção
🗄️
Conexão Supabase
Não configurado
Empresa ID: —
Usuário: —
📋
SQL — Execute no Supabase
Database → SQL Editor → New query → Cole e execute.
-- Execute no Supabase SQL Editor
-- (10 tabelas + RLS + função multiempresa)
CREATE TABLE IF NOT EXISTS empresas (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
nome TEXT NOT NULL, email TEXT, telefone TEXT,
cnpj TEXT, cidade TEXT, obs TEXT, logo_url TEXT,
plano TEXT DEFAULT 'free', ativo BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS usuarios_empresa (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
role TEXT DEFAULT 'owner',
created_at TIMESTAMPTZ DEFAULT now(),
UNIQUE(user_id, empresa_id)
);
CREATE TABLE IF NOT EXISTS clientes (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
nome TEXT NOT NULL, telefone TEXT, email TEXT,
cpf_cnpj TEXT, endereco TEXT, cidade TEXT, obs TEXT,
ativo BOOLEAN DEFAULT true, created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS materiais (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
nome TEXT NOT NULL, tipo TEXT DEFAULT 'Impressão',
custo_m2 NUMERIC DEFAULT 0, preco_m2 NUMERIC DEFAULT 0,
markup NUMERIC DEFAULT 0, largura_rolo NUMERIC,
ativo BOOLEAN DEFAULT true, created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS orcamentos (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
cliente_id UUID REFERENCES clientes(id),
numero TEXT, cliente_nome TEXT, telefone TEXT,
produto TEXT, material TEXT,
largura NUMERIC DEFAULT 0, altura NUMERIC DEFAULT 0,
qtd INTEGER DEFAULT 1, area NUMERIC DEFAULT 0,
custo_total NUMERIC DEFAULT 0, preco_final NUMERIC DEFAULT 0,
lucro NUMERIC DEFAULT 0, margem_real NUMERIC DEFAULT 0,
margem NUMERIC DEFAULT 40, desconto NUMERIC DEFAULT 0,
prazo TEXT, pagamento TEXT, validade TEXT DEFAULT '7',
obs TEXT, status TEXT DEFAULT 'orcamento',
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS itens_orcamento (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
orcamento_id UUID REFERENCES orcamentos(id) ON DELETE CASCADE,
seq INTEGER DEFAULT 1, descricao TEXT NOT NULL,
quantidade NUMERIC DEFAULT 1, unidade TEXT DEFAULT 'm2',
valor_unitario NUMERIC DEFAULT 0, valor_total NUMERIC DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS pedidos (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
orcamento_id UUID REFERENCES orcamentos(id),
cliente_id UUID REFERENCES clientes(id),
numero TEXT, cliente_nome TEXT, produto TEXT, material TEXT,
largura NUMERIC DEFAULT 0, altura NUMERIC DEFAULT 0, area NUMERIC DEFAULT 0,
valor NUMERIC DEFAULT 0, status TEXT DEFAULT 'producao',
prioridade TEXT DEFAULT 'normal',
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS financeiro (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
orcamento_id UUID REFERENCES orcamentos(id),
pedido_id UUID REFERENCES pedidos(id),
tipo TEXT NOT NULL, categoria TEXT DEFAULT 'outros',
descricao TEXT NOT NULL, valor NUMERIC DEFAULT 0,
status TEXT DEFAULT 'pendente', vencimento DATE, pago_em DATE,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS notas_fiscais (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
orcamento_id UUID REFERENCES orcamentos(id),
pedido_id UUID REFERENCES pedidos(id),
cliente_nome TEXT, numero_nf TEXT, serie TEXT DEFAULT '1',
chave_acesso TEXT, xml_url TEXT, pdf_url TEXT,
valor NUMERIC DEFAULT 0, status TEXT DEFAULT 'pendente',
resposta_api TEXT, created_at TIMESTAMPTZ DEFAULT now()
);
-- Função helper multiempresa (RLS)
CREATE OR REPLACE FUNCTION get_empresa_id()
RETURNS UUID AS $$
SELECT empresa_id FROM usuarios_empresa
WHERE user_id = auth.uid() LIMIT 1;
$$ LANGUAGE SQL SECURITY DEFINER STABLE;
-- Ativar RLS em todas as tabelas
ALTER TABLE empresas ENABLE ROW LEVEL SECURITY;
ALTER TABLE usuarios_empresa ENABLE ROW LEVEL SECURITY;
ALTER TABLE clientes ENABLE ROW LEVEL SECURITY;
ALTER TABLE materiais ENABLE ROW LEVEL SECURITY;
ALTER TABLE orcamentos ENABLE ROW LEVEL SECURITY;
ALTER TABLE itens_orcamento ENABLE ROW LEVEL SECURITY;
ALTER TABLE pedidos ENABLE ROW LEVEL SECURITY;
ALTER TABLE financeiro ENABLE ROW LEVEL SECURITY;
ALTER TABLE notas_fiscais ENABLE ROW LEVEL SECURITY;
-- Políticas (RLS) — cada empresa vê só seus dados
DO $$ BEGIN
DROP POLICY IF EXISTS "own_empresas" ON empresas;
DROP POLICY IF EXISTS "own_usuarios_empresa" ON usuarios_empresa;
DROP POLICY IF EXISTS "own_clientes" ON clientes;
DROP POLICY IF EXISTS "own_materiais" ON materiais;
DROP POLICY IF EXISTS "own_orcamentos" ON orcamentos;
DROP POLICY IF EXISTS "own_itens_orcamento" ON itens_orcamento;
DROP POLICY IF EXISTS "own_pedidos" ON pedidos;
DROP POLICY IF EXISTS "own_financeiro" ON financeiro;
DROP POLICY IF EXISTS "own_notas_fiscais" ON notas_fiscais;
END $$;
CREATE POLICY "own_empresas" ON empresas FOR ALL
USING (id = get_empresa_id()) WITH CHECK (id = get_empresa_id());
CREATE POLICY "own_usuarios_empresa" ON usuarios_empresa FOR ALL
USING (user_id = auth.uid());
CREATE POLICY "own_clientes" ON clientes FOR ALL
USING (empresa_id = get_empresa_id()) WITH CHECK (empresa_id = get_empresa_id());
CREATE POLICY "own_materiais" ON materiais FOR ALL
USING (empresa_id = get_empresa_id()) WITH CHECK (empresa_id = get_empresa_id());
CREATE POLICY "own_orcamentos" ON orcamentos FOR ALL
USING (empresa_id = get_empresa_id()) WITH CHECK (empresa_id = get_empresa_id());
CREATE POLICY "own_itens_orcamento" ON itens_orcamento FOR ALL
USING (empresa_id = get_empresa_id()) WITH CHECK (empresa_id = get_empresa_id());
CREATE POLICY "own_pedidos" ON pedidos FOR ALL
USING (empresa_id = get_empresa_id()) WITH CHECK (empresa_id = get_empresa_id());
CREATE POLICY "own_financeiro" ON financeiro FOR ALL
USING (empresa_id = get_empresa_id()) WITH CHECK (empresa_id = get_empresa_id());
CREATE POLICY "own_notas_fiscais" ON notas_fiscais FOR ALL
USING (empresa_id = get_empresa_id()) WITH CHECK (empresa_id = get_empresa_id());
-- ─── TABELAS SAAS (assinaturas, pagamentos, planos) ────────────────
CREATE TABLE IF NOT EXISTS assinaturas (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
plano TEXT DEFAULT 'free',
status TEXT DEFAULT 'ativa',
valor NUMERIC DEFAULT 0,
data_inicio TIMESTAMPTZ DEFAULT now(),
data_expiracao TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT now(),
UNIQUE(empresa_id)
);
CREATE TABLE IF NOT EXISTS pagamentos (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
assinatura_id UUID REFERENCES assinaturas(id),
valor NUMERIC DEFAULT 0,
metodo TEXT DEFAULT 'pix',
status TEXT DEFAULT 'pendente',
descricao TEXT,
referencia TEXT,
data_pagamento TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS planos (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
nome TEXT NOT NULL UNIQUE,
nome_interno TEXT,
valor NUMERIC DEFAULT 0,
cor TEXT DEFAULT '#8892a8',
descricao TEXT,
features JSONB,
limite_usuarios INTEGER DEFAULT -1,
recursos JSONB,
ativo BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS checklist_producao (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
pedido_id UUID REFERENCES pedidos(id) ON DELETE CASCADE,
etapa TEXT NOT NULL,
ordem INTEGER DEFAULT 1,
concluido BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT now()
);
-- RLS para tabelas SaaS
ALTER TABLE assinaturas ENABLE ROW LEVEL SECURITY;
ALTER TABLE pagamentos ENABLE ROW LEVEL SECURITY;
ALTER TABLE planos ENABLE ROW LEVEL SECURITY;
ALTER TABLE checklist_producao ENABLE ROW LEVEL SECURITY;
DO $$ BEGIN
DROP POLICY IF EXISTS "own_assinaturas" ON assinaturas;
DROP POLICY IF EXISTS "own_pagamentos" ON pagamentos;
DROP POLICY IF EXISTS "read_planos" ON planos;
DROP POLICY IF EXISTS "own_checklist" ON checklist_producao;
END $$;
CREATE POLICY "own_assinaturas" ON assinaturas FOR ALL
USING (empresa_id = get_empresa_id()) WITH CHECK (empresa_id = get_empresa_id());
CREATE POLICY "own_pagamentos" ON pagamentos FOR ALL
USING (empresa_id = get_empresa_id()) WITH CHECK (empresa_id = get_empresa_id());
CREATE POLICY "read_planos" ON planos FOR SELECT USING (true);
CREATE POLICY "own_checklist" ON checklist_producao FOR ALL
USING (empresa_id = get_empresa_id()) WITH CHECK (empresa_id = get_empresa_id());
-- Fornecedores, Insumos, Compras (tabelas ERP v4)
CREATE TABLE IF NOT EXISTS fornecedores (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
nome TEXT NOT NULL, cnpj TEXT, telefone TEXT, email TEXT,
cidade TEXT, prazo_entrega INTEGER DEFAULT 0,
observacoes TEXT, ativo BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS insumos (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
fornecedor_id UUID REFERENCES fornecedores(id),
nome TEXT NOT NULL, codigo TEXT, unidade TEXT DEFAULT 'm²',
custo_unitario NUMERIC DEFAULT 0, preco_venda NUMERIC DEFAULT 0,
estoque_atual NUMERIC DEFAULT 0, estoque_minimo NUMERIC DEFAULT 0,
categoria TEXT DEFAULT 'Outros', ativo BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS compras (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
fornecedor_id UUID REFERENCES fornecedores(id),
numero TEXT, data DATE, valor_total NUMERIC DEFAULT 0,
status TEXT DEFAULT 'pendente', nota_fiscal TEXT, observacoes TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS compra_itens (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
compra_id UUID REFERENCES compras(id) ON DELETE CASCADE,
insumo_id UUID REFERENCES insumos(id),
descricao TEXT, quantidade NUMERIC DEFAULT 0,
custo_unitario NUMERIC DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS estoque_movimentos (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
insumo_id UUID REFERENCES insumos(id),
tipo TEXT NOT NULL, quantidade NUMERIC DEFAULT 0,
custo_unitario NUMERIC DEFAULT 0, custo_total NUMERIC DEFAULT 0,
motivo TEXT, referencia_id TEXT, referencia_tipo TEXT, observacoes TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS produtos_cat (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
nome TEXT NOT NULL, tipo TEXT DEFAULT 'Banner', descricao TEXT,
materiais_padrao JSONB, acabamentos_pad JSONB, formula JSONB,
ativo BOOLEAN DEFAULT true, created_at TIMESTAMPTZ DEFAULT now()
);
-- Índices de performance
CREATE INDEX IF NOT EXISTS idx_ue_user ON usuarios_empresa(user_id);
CREATE INDEX IF NOT EXISTS idx_orc_emp ON orcamentos(empresa_id, status);
CREATE INDEX IF NOT EXISTS idx_ped_emp ON pedidos(empresa_id, status);
CREATE INDEX IF NOT EXISTS idx_fin_emp ON financeiro(empresa_id, tipo, status);
CREATE INDEX IF NOT EXISTS idx_ass_emp ON assinaturas(empresa_id, status);
CREATE INDEX IF NOT EXISTS idx_pag_emp ON pagamentos(empresa_id, status);
CREATE INDEX IF NOT EXISTS idx_ins_emp ON insumos(empresa_id);
CREATE INDEX IF NOT EXISTS idx_chk_ped ON checklist_producao(pedido_id);
🔧
SQL — Corrigir Permissões + Ativar Premium
⚠️ Execute se aparecer erro 403 ou "permission denied". Também ativa o plano Avançado.
-- ═══════════════════════════════════════════════════════════════════════
-- VisualCalc ERP SaaS Pro v10 — SQL COMPLETO PRONTO PARA PRODUÇÃO
-- Execute: Supabase → SQL Editor → New query → Run (All)
-- ═══════════════════════════════════════════════════════════════════════
-- ── 1. CRIAR TABELAS QUE FALTAM ──────────────────────────────────────
CREATE TABLE IF NOT EXISTS assinaturas (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
plano TEXT DEFAULT 'free',
status TEXT DEFAULT 'ativa',
valor NUMERIC DEFAULT 0,
data_inicio TIMESTAMPTZ DEFAULT now(),
data_expiracao TIMESTAMPTZ,
stripe_customer_id TEXT,
stripe_subscription_id TEXT,
stripe_price_id TEXT,
trial_end TIMESTAMPTZ,
cancel_at_period_end BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT now(),
UNIQUE(empresa_id)
);
CREATE TABLE IF NOT EXISTS pagamentos (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
assinatura_id UUID REFERENCES assinaturas(id),
valor NUMERIC DEFAULT 0,
metodo TEXT DEFAULT 'pix',
status TEXT DEFAULT 'pendente',
descricao TEXT,
referencia TEXT,
stripe_invoice_id TEXT,
stripe_payment_id TEXT,
data_pagamento TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS planos (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
nome TEXT NOT NULL UNIQUE, nome_interno TEXT,
valor NUMERIC DEFAULT 0, cor TEXT DEFAULT '#8892a8',
descricao TEXT, features JSONB, limite_usuarios INTEGER DEFAULT -1,
recursos JSONB, ativo BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS checklist_producao (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
pedido_id UUID REFERENCES pedidos(id) ON DELETE CASCADE,
etapa TEXT NOT NULL, ordem INTEGER DEFAULT 1,
concluido BOOLEAN DEFAULT false, created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS fornecedores (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
nome TEXT NOT NULL, cnpj TEXT, telefone TEXT, email TEXT,
cidade TEXT, prazo_entrega INTEGER DEFAULT 0,
observacoes TEXT, ativo BOOLEAN DEFAULT true, created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS insumos (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
fornecedor_id UUID REFERENCES fornecedores(id),
nome TEXT NOT NULL, codigo TEXT, unidade TEXT DEFAULT 'm²',
custo_unitario NUMERIC DEFAULT 0, preco_venda NUMERIC DEFAULT 0,
estoque_atual NUMERIC DEFAULT 0, estoque_minimo NUMERIC DEFAULT 0,
categoria TEXT DEFAULT 'Outros', ativo BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS compras (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
fornecedor_id UUID REFERENCES fornecedores(id),
numero TEXT, data DATE, valor_total NUMERIC DEFAULT 0,
status TEXT DEFAULT 'pendente', nota_fiscal TEXT, observacoes TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS compra_itens (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
compra_id UUID REFERENCES compras(id) ON DELETE CASCADE,
insumo_id UUID REFERENCES insumos(id),
descricao TEXT, quantidade NUMERIC DEFAULT 0,
custo_unitario NUMERIC DEFAULT 0, created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS estoque_movimentos (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
insumo_id UUID REFERENCES insumos(id),
tipo TEXT NOT NULL, quantidade NUMERIC DEFAULT 0,
custo_unitario NUMERIC DEFAULT 0, custo_total NUMERIC DEFAULT 0,
motivo TEXT, referencia_id TEXT, referencia_tipo TEXT,
observacoes TEXT, created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS produtos_cat (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
empresa_id UUID REFERENCES empresas(id) ON DELETE CASCADE,
nome TEXT NOT NULL, tipo TEXT DEFAULT 'Banner', descricao TEXT,
materiais_padrao JSONB, acabamentos_pad JSONB, formula JSONB,
ativo BOOLEAN DEFAULT true, created_at TIMESTAMPTZ DEFAULT now()
);
-- ── 2. GARANTIR COLUNAS ────────────────────────────────────────────
ALTER TABLE assinaturas ADD COLUMN IF NOT EXISTS valor NUMERIC DEFAULT 0;
ALTER TABLE assinaturas ADD COLUMN IF NOT EXISTS stripe_customer_id TEXT;
ALTER TABLE assinaturas ADD COLUMN IF NOT EXISTS stripe_subscription_id TEXT;
ALTER TABLE assinaturas ADD COLUMN IF NOT EXISTS stripe_price_id TEXT;
ALTER TABLE assinaturas ADD COLUMN IF NOT EXISTS trial_end TIMESTAMPTZ;
ALTER TABLE assinaturas ADD COLUMN IF NOT EXISTS cancel_at_period_end BOOLEAN DEFAULT false;
ALTER TABLE pagamentos ADD COLUMN IF NOT EXISTS stripe_invoice_id TEXT;
ALTER TABLE pagamentos ADD COLUMN IF NOT EXISTS stripe_payment_id TEXT;
ALTER TABLE assinaturas ADD COLUMN IF NOT EXISTS data_inicio TIMESTAMPTZ DEFAULT now();
ALTER TABLE assinaturas ADD COLUMN IF NOT EXISTS data_expiracao TIMESTAMPTZ;
ALTER TABLE pagamentos ADD COLUMN IF NOT EXISTS metodo TEXT DEFAULT 'pix';
ALTER TABLE pagamentos ADD COLUMN IF NOT EXISTS descricao TEXT;
ALTER TABLE pagamentos ADD COLUMN IF NOT EXISTS data_pagamento TIMESTAMPTZ;
ALTER TABLE pagamentos ADD COLUMN IF NOT EXISTS referencia TEXT;
ALTER TABLE pagamentos ADD COLUMN IF NOT EXISTS assinatura_id UUID;
ALTER TABLE planos ADD COLUMN IF NOT EXISTS features JSONB;
ALTER TABLE planos ADD COLUMN IF NOT EXISTS nome_interno TEXT;
ALTER TABLE planos ADD COLUMN IF NOT EXISTS cor TEXT DEFAULT '#8892a8';
-- ── 3. FUNÇÃO get_empresa_id() ────────────────────────────────────
CREATE OR REPLACE FUNCTION get_empresa_id()
RETURNS UUID LANGUAGE sql STABLE SECURITY DEFINER AS $$
SELECT empresa_id FROM usuarios_empresa WHERE user_id = auth.uid() LIMIT 1;
$$;
-- ── 4. PERMISSÕES ──────────────────────────────────────────────────
GRANT USAGE ON SCHEMA public TO authenticated, anon;
GRANT SELECT, INSERT, UPDATE ON empresas, usuarios_empresa, assinaturas, pagamentos TO authenticated;
GRANT SELECT, INSERT, UPDATE, DELETE ON orcamentos, itens_orcamento, clientes, materiais, pedidos, financeiro, notas_fiscais, fornecedores, insumos, compras, compra_itens, estoque_movimentos, produtos_cat, checklist_producao TO authenticated;
GRANT SELECT ON planos TO authenticated;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO authenticated;
-- ── 5. RLS — TODAS AS TABELAS ──────────────────────────────────────
DO $$
DECLARE t TEXT;
BEGIN
-- Desabilitar + Limpar + Recriar para cada tabela
FOREACH t IN ARRAY ARRAY[
'empresas','usuarios_empresa','assinaturas','pagamentos',
'orcamentos','itens_orcamento','clientes','materiais','pedidos',
'financeiro','notas_fiscais','fornecedores','insumos',
'compras','compra_itens','estoque_movimentos','produtos_cat',
'checklist_producao'
]
LOOP
EXECUTE format('ALTER TABLE %I DISABLE ROW LEVEL SECURITY', t);
EXECUTE format('DROP POLICY IF EXISTS "own_%1$s" ON %1$I', t);
EXECUTE format('DROP POLICY IF EXISTS "select_%1$s" ON %1$I', t);
EXECUTE format('DROP POLICY IF EXISTS "insert_%1$s" ON %1$I', t);
EXECUTE format('DROP POLICY IF EXISTS "update_%1$s" ON %1$I', t);
EXECUTE format('DROP POLICY IF EXISTS "read_%1$s" ON %1$I', t);
END LOOP;
END;
$$;
-- RLS: Empresas (pode inserir nova, só vê a própria)
ALTER TABLE empresas DISABLE ROW LEVEL SECURITY;
CREATE POLICY "select_empresas" ON empresas FOR SELECT USING (id = get_empresa_id());
CREATE POLICY "insert_empresas" ON empresas FOR INSERT WITH CHECK (true);
CREATE POLICY "update_empresas" ON empresas FOR UPDATE USING (id = get_empresa_id());
ALTER TABLE empresas ENABLE ROW LEVEL SECURITY;
-- RLS: usuarios_empresa
ALTER TABLE usuarios_empresa DISABLE ROW LEVEL SECURITY;
CREATE POLICY "own_usuarios_empresa" ON usuarios_empresa FOR ALL
USING (empresa_id = get_empresa_id() OR user_id = auth.uid())
WITH CHECK (empresa_id = get_empresa_id());
ALTER TABLE usuarios_empresa ENABLE ROW LEVEL SECURITY;
-- RLS: planos (leitura pública)
ALTER TABLE planos DISABLE ROW LEVEL SECURITY;
CREATE POLICY "read_planos" ON planos FOR SELECT USING (true);
ALTER TABLE planos ENABLE ROW LEVEL SECURITY;
-- RLS: todas as outras tabelas — padrão empresa_id
DO $$
DECLARE t TEXT;
BEGIN
FOREACH t IN ARRAY ARRAY[
'assinaturas','pagamentos','orcamentos','itens_orcamento',
'clientes','materiais','pedidos','financeiro','notas_fiscais',
'fornecedores','insumos','compras','compra_itens',
'estoque_movimentos','produtos_cat','checklist_producao'
]
LOOP
EXECUTE format(
'CREATE POLICY "own_%1$s" ON %1$I FOR ALL
USING (empresa_id = get_empresa_id())
WITH CHECK (empresa_id = get_empresa_id())',
t
);
EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', t);
END LOOP;
END;
$$;
-- ── 6. REMOVER ASSINATURAS DUPLICADAS ─────────────────────────────
DELETE FROM assinaturas WHERE id NOT IN (
SELECT DISTINCT ON (empresa_id) id FROM assinaturas
ORDER BY empresa_id, created_at DESC
);
-- ── 7. UPSERT PLANOS ───────────────────────────────────────────────
INSERT INTO planos (nome, nome_interno, valor, cor, features) VALUES
('Básico', 'free', 0, '#8892a8', '{"modulos":["dashboard","orcamento","historico","clientes","materiais"]}'::jsonb),
('Médio', 'pro', 97, '#e8b84b', '{"modulos":["dashboard","orcamento","historico","clientes","materiais","pedidos","financeiro","insumos","estoque","compras","fornecedores","produtos"]}'::jsonb),
('Avançado', 'premium', 197, '#8b5cf6', '{"modulos":["dashboard","orcamento","historico","clientes","materiais","pedidos","financeiro","insumos","estoque","compras","fornecedores","produtos","relatorios","nf","pcp"]}'::jsonb)
ON CONFLICT (nome) DO UPDATE SET
nome_interno = EXCLUDED.nome_interno, valor = EXCLUDED.valor,
cor = EXCLUDED.cor, features = EXCLUDED.features;
-- ── 8. ATIVAR PREMIUM NA SUA CONTA (OWNER) ──────────────────────
ALTER TABLE empresas DISABLE ROW LEVEL SECURITY;
UPDATE empresas SET plano = 'premium';
ALTER TABLE empresas ENABLE ROW LEVEL SECURITY;
INSERT INTO assinaturas (empresa_id, plano, status, valor, data_inicio, data_expiracao)
SELECT id, 'premium', 'ativa', 0, now(), now() + interval '3650 days'
FROM empresas
ON CONFLICT (empresa_id) DO UPDATE SET
plano = 'premium', status = 'ativa',
data_expiracao = now() + interval '3650 days';
-- ── 9. FUNÇÃO trocar_plano() ──────────────────────────────────────
CREATE OR REPLACE FUNCTION trocar_plano(novo_plano TEXT)
RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$
DECLARE emp_id UUID := get_empresa_id();
BEGIN
UPDATE empresas SET plano = novo_plano WHERE id = emp_id;
INSERT INTO assinaturas (empresa_id, plano, status, data_inicio, data_expiracao)
VALUES (emp_id, novo_plano, 'ativa', now(), now() + interval '30 days')
ON CONFLICT (empresa_id) DO UPDATE SET
plano = novo_plano, status = 'ativa',
data_expiracao = now() + interval '30 days';
END;
$$;
-- ── 10. ÍNDICES ────────────────────────────────────────────────────
CREATE INDEX IF NOT EXISTS idx_ue_user ON usuarios_empresa(user_id);
CREATE INDEX IF NOT EXISTS idx_orc_emp ON orcamentos(empresa_id, status);
CREATE INDEX IF NOT EXISTS idx_ped_emp ON pedidos(empresa_id, status);
CREATE INDEX IF NOT EXISTS idx_fin_emp ON financeiro(empresa_id, tipo, status);
CREATE INDEX IF NOT EXISTS idx_ass_emp ON assinaturas(empresa_id, status);
CREATE INDEX IF NOT EXISTS idx_pag_emp ON pagamentos(empresa_id, status);
CREATE INDEX IF NOT EXISTS idx_ins_emp ON insumos(empresa_id);
CREATE INDEX IF NOT EXISTS idx_chk_ped ON checklist_producao(pedido_id);
-- ── 11. FUNÇÃO AUXILIAR: owner lê todas empresas ─────────────────
-- Necessária para o painel Admin SaaS do owner
CREATE OR REPLACE FUNCTION is_owner_user()
RETURNS BOOLEAN LANGUAGE sql STABLE SECURITY DEFINER AS $$
SELECT EXISTS (
SELECT 1 FROM usuarios_empresa
WHERE user_id = auth.uid() AND role = 'owner'
);
$$;
-- Política adicional: owner pode ver todas assinaturas e pagamentos
DO $$ BEGIN
DROP POLICY IF EXISTS "owner_all_assinaturas" ON assinaturas;
DROP POLICY IF EXISTS "owner_all_pagamentos" ON pagamentos;
DROP POLICY IF EXISTS "owner_all_empresas" ON empresas;
END $$;
CREATE POLICY "owner_all_assinaturas" ON assinaturas FOR SELECT
USING (is_owner_user() OR empresa_id = get_empresa_id());
CREATE POLICY "owner_all_pagamentos" ON pagamentos FOR SELECT
USING (is_owner_user() OR empresa_id = get_empresa_id());
CREATE POLICY "owner_all_empresas" ON empresas FOR SELECT
USING (is_owner_user() OR id = get_empresa_id());
-- ── 12. FUNÇÃO pode_acessar_modulo (validação server-side) ──────────
-- Chamável via RPC do frontend: supabase.rpc('pode_acessar_modulo', {modulo:'pcp'})
-- NÃO depende do frontend. Valida plano no banco usando auth.uid().
CREATE OR REPLACE FUNCTION pode_acessar_modulo(modulo TEXT)
RETURNS BOOLEAN LANGUAGE plpgsql SECURITY DEFINER AS $$
DECLARE
v_empresa_id UUID;
v_role TEXT;
v_plano TEXT;
v_status TEXT;
v_expiracao TIMESTAMPTZ;
BEGIN
-- Buscar empresa e role do usuário autenticado
SELECT ue.empresa_id, ue.role
INTO v_empresa_id, v_role
FROM usuarios_empresa ue
WHERE ue.user_id = auth.uid()
LIMIT 1;
IF v_empresa_id IS NULL THEN RETURN FALSE; END IF;
-- Owner: acesso total (definido no banco, não no frontend)
IF v_role = 'owner' THEN RETURN TRUE; END IF;
-- Buscar assinatura ativa
SELECT a.plano, a.status, a.data_expiracao
INTO v_plano, v_status, v_expiracao
FROM assinaturas a
WHERE a.empresa_id = v_empresa_id
AND a.status = 'ativa'
ORDER BY a.created_at DESC
LIMIT 1;
-- Sem assinatura ativa: bloquear
IF v_plano IS NULL OR v_status != 'ativa' THEN RETURN FALSE; END IF;
-- Assinatura expirada: bloquear
IF v_expiracao IS NOT NULL AND v_expiracao < NOW() THEN RETURN FALSE; END IF;
-- Regras por módulo e plano
RETURN CASE modulo
WHEN 'dashboard' THEN TRUE
WHEN 'orcamento' THEN TRUE
WHEN 'historico' THEN TRUE
WHEN 'clientes' THEN TRUE
WHEN 'materiais' THEN TRUE
WHEN 'config' THEN TRUE
WHEN 'pedidos' THEN v_plano IN ('medio','pro','avancado','premium')
WHEN 'financeiro' THEN v_plano IN ('medio','pro','avancado','premium')
WHEN 'insumos' THEN v_plano IN ('medio','pro','avancado','premium')
WHEN 'estoque' THEN v_plano IN ('medio','pro','avancado','premium')
WHEN 'compras' THEN v_plano IN ('medio','pro','avancado','premium')
WHEN 'fornecedores' THEN v_plano IN ('medio','pro','avancado','premium')
WHEN 'produtos' THEN v_plano IN ('medio','pro','avancado','premium')
WHEN 'relatorios' THEN v_plano IN ('avancado','premium')
WHEN 'nf' THEN v_plano IN ('avancado','premium')
WHEN 'pcp' THEN v_plano IN ('avancado','premium')
ELSE FALSE
END;
END;
$$;
-- Permissão: usuários autenticados podem chamar a função
GRANT EXECUTE ON FUNCTION pode_acessar_modulo(TEXT) TO authenticated;
-- ── 13. TABELA logs_seguranca ─────────────────────────────────────
CREATE TABLE IF NOT EXISTS logs_seguranca (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id),
empresa_id UUID REFERENCES empresas(id),
acao TEXT NOT NULL,
detalhes JSONB,
ip TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_log_user ON logs_seguranca(user_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_log_acao ON logs_seguranca(acao, created_at DESC);
ALTER TABLE logs_seguranca ENABLE ROW LEVEL SECURITY;
-- Usuário só vê seus próprios logs; owner vê todos
DROP POLICY IF EXISTS "own_logs" ON logs_seguranca;
CREATE POLICY "own_logs" ON logs_seguranca FOR ALL
USING (user_id = auth.uid() OR is_owner_user());
-- ── 14. FUNÇÕES SQL DE MÉTRICAS (usadas via RPC) ──────────────────
-- MRR: soma das assinaturas ativas com valor > 0
CREATE OR REPLACE FUNCTION calc_mrr()
RETURNS NUMERIC LANGUAGE sql STABLE SECURITY DEFINER AS $$
SELECT COALESCE(SUM(valor), 0)
FROM assinaturas
WHERE status = 'ativa'
AND (data_expiracao IS NULL OR data_expiracao > NOW());
$$;
-- Churn Rate: % de assinaturas canceladas/vencidas
CREATE OR REPLACE FUNCTION calc_churn()
RETURNS NUMERIC LANGUAGE sql STABLE SECURITY DEFINER AS $$
SELECT CASE
WHEN COUNT(*) = 0 THEN 0
ELSE ROUND(
COUNT(*) FILTER (WHERE status IN ('cancelada','vencida'))::NUMERIC
/ COUNT(*)::NUMERIC * 100, 1
)
END
FROM assinaturas;
$$;
-- Novos clientes no mês atual
CREATE OR REPLACE FUNCTION calc_novos_mes()
RETURNS INTEGER LANGUAGE sql STABLE SECURITY DEFINER AS $$
SELECT COUNT(*)::INTEGER
FROM empresas
WHERE created_at >= date_trunc('month', NOW());
$$;
-- Receita do mês (pagamentos confirmados)
CREATE OR REPLACE FUNCTION calc_receita_mes()
RETURNS NUMERIC LANGUAGE sql STABLE SECURITY DEFINER AS $$
SELECT COALESCE(SUM(valor), 0)
FROM pagamentos
WHERE status = 'pago'
AND created_at >= date_trunc('month', NOW());
$$;
-- Verificar e vencer assinaturas expiradas (chamada via cron ou manualmente)
CREATE OR REPLACE FUNCTION verificar_assinaturas_expiradas()
RETURNS INTEGER LANGUAGE plpgsql SECURITY DEFINER AS $$
DECLARE
v_count INTEGER;
BEGIN
UPDATE assinaturas
SET status = 'vencida'
WHERE status = 'ativa'
AND data_expiracao IS NOT NULL
AND data_expiracao < NOW();
GET DIAGNOSTICS v_count = ROW_COUNT;
RETURN v_count;
END;
$$;
-- Permissões para as funções de métricas
GRANT EXECUTE ON FUNCTION calc_mrr() TO authenticated;
GRANT EXECUTE ON FUNCTION calc_churn() TO authenticated;
GRANT EXECUTE ON FUNCTION calc_novos_mes() TO authenticated;
GRANT EXECUTE ON FUNCTION calc_receita_mes() TO authenticated;
GRANT EXECUTE ON FUNCTION verificar_assinaturas_expiradas() TO authenticated;
-- ── 15. BLOQUEAR UPDATE direto em assinaturas (só via função) ─────
-- Remove permissão de UPDATE direto para usuários comuns
-- (só service_role pode atualizar; frontend usa funções SECURITY DEFINER)
DROP POLICY IF EXISTS "no_direct_update_assinaturas" ON assinaturas;
CREATE POLICY "no_direct_update_assinaturas" ON assinaturas FOR UPDATE
USING (is_owner_user()); -- só owner pode UPDATE direto; outros via função
-- ── 16. VERIFICAR RESULTADO ────────────────────────────────────────
SELECT e.nome, e.plano, a.plano AS ass_plano, a.status, a.data_expiracao::date AS expira
FROM empresas e LEFT JOIN assinaturas a ON a.empresa_id = e.id
ORDER BY a.created_at DESC LIMIT 10;
💡 "Ativar Avançado AGORA" funciona imediatamente sem banco. Execute o SQL para tornar permanente.
✅ Tudo do Pro ✅ Usuários ilimitados ✅ API access ✅ Nota Fiscal integrada ✅ Suporte prioritário
💾
Exportar Dados
⬇️
Restaurar Backup
⚠️ Substituirá todos os dados locais.
📖 Central de Ajuda
Guias, tutoriais e instruções do VisualCalc ERP
Carregando tutoriais...
👤 Cadastrar Cliente
Dados do cliente para orçamentos
💰 Novo Lançamento
Registrar entrada ou saída no fluxo de caixa
🏢 Dados da Empresa
Aparecem no cabeçalho dos PDFs e orçamentos
Sem logo ·
📤 Enviar Orçamento
Escolha como enviar a proposta
🗄️ Configurar Supabase
Conecte ao banco de dados para sincronizar dados
1. supabase.com → Criar projeto gratuito
2. Settings → API → copiar URL e chave anon
3. SQL Editor → executar o SQL da aba Configurações
4. Fazer login no sistema
💳 Pagamento via PIX
Renovar Assinatura
Valor a pagar
R$ 0,00
🔑 Chave PIX — Clique para copiar
—
Banco: — · Beneficiário: —
1
Abra seu app de banco e acesse a opção PIX
2
Copie a chave PIX acima e cole no campo "Pix Copia e Cola" ou use a chave manualmente
3
Realize o pagamento do valor exato indicado
4
Clique em "Já realizei o pagamento" — o admin confirmará em até 24h
✅ Confirmar Pagamento
Confirme apenas após verificar o recebimento do PIX
⚠️ Esta ação renovará a assinatura por 30 dias automaticamente.
Empresa: —
Valor: —
+ Criar Novo Plano
Apenas o proprietário (owner) pode salvar alterações de planos
💡 Módulos marcados ficam acessíveis. Desmarcados aparecem com 🔒 na sidebar do cliente.
Gerenciar Cliente
Empresa-
Email-
Plano atual-
+ Novo Fornecedor
+ Novo Insumo
📦 Movimentação de Estoque
—
Estoque atual: —
🛒 Nova Compra
Adicionar Itens
📦 Novo Produto
Configure produto, dados fiscais e parâmetros de cálculo
Código para controle interno e rastreamento
Produtos unitários não usam dimensões de m²
Ignora cálculo de custo — usa este valor direto
🧾
Dados obrigatórios para emissão de NF-e Preencha NCM, CFOP e CST corretamente para evitar rejeição na SEFAZ
Para a maioria das gráficas: 5102
Apenas se houver benefício fiscal estadual
Para Simples Nacional geralmente: 0
Resumo para NF-e
NCM
—
CFOP
—
CST/CSOSN
—
Unidade
—
Markup aplicado automaticamente no wizard de orçamento
Alerta se o preço cair abaixo desta margem
Preço nunca fica abaixo deste valor
Por m² calculado nunca fica abaixo deste valor
Bloqueia descontos acima deste limite
Simulação de Preço (1m²)
Custo: R$ —
Markup: —%
Preço: R$ —
Margem: —%
Velocidade padrão da máquina para este produto
+ Novo Produto Fiscal
Preencha os dados fiscais para emissão de NF
💳 Pagamento via PIX
Pague com PIX e ative seu plano instantaneamente
🤖
Assistente IA — VisualCalc
Conectado via OpenAI
⚠️ Configure sua OpenAI API Key para usar o assistente
A chave é salva apenas no seu navegador (localStorage)
Olá! Sou o assistente IA do VisualCalc ERP. Posso ajudar você a:
• 💰 Sugerir preços para seus produtos
• 📐 Calcular custos de metalon e produção
• 📊 Analisar margens e lucratividade
• 🔄 Otimizar seu fluxo de produção
Me descreva seu produto ou diga o que precisa calcular!