chore: sincroniza projeto para gitea
This commit is contained in:
803
server.js
803
server.js
@@ -2,20 +2,25 @@ const express = require('express');
|
||||
const cors = require('cors');
|
||||
const path = require('path');
|
||||
const multer = require('multer');
|
||||
const sqlite3 = require('sqlite3').verbose();
|
||||
const { createClient } = require('@supabase/supabase-js');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
require('dotenv').config();
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 5000;
|
||||
|
||||
// Middleware
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
app.use('/uploads', express.static('uploads'));
|
||||
app.use(express.static(path.join(__dirname, 'client/build')));
|
||||
|
||||
// Configuração do multer para upload de imagens
|
||||
// Configuração do Supabase
|
||||
const supabaseUrl = 'https://ydhzylfnpqlxnzfcclla.supabase.co'
|
||||
const supabaseKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InlkaHp5bGZucHFseG56ZmNjbGxhIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjA1NDA1NjIsImV4cCI6MjA3NjExNjU2Mn0.gIHxyAYngqkJ8z2Gt5ESYmG605vhY_LGTQB7Cjp4ZTA'
|
||||
|
||||
const supabase = createClient(supabaseUrl, supabaseKey)
|
||||
|
||||
// Configuração do Multer para upload de arquivos
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
cb(null, 'uploads/');
|
||||
@@ -38,583 +43,301 @@ const upload = multer({
|
||||
limits: { fileSize: 5 * 1024 * 1024 } // 5MB
|
||||
});
|
||||
|
||||
// Inicializar banco de dados
|
||||
const db = new sqlite3.Database('./liberi_kids.db', (err) => {
|
||||
if (err) {
|
||||
console.error('Erro ao conectar com o banco de dados:', err.message);
|
||||
} else {
|
||||
console.log('Conectado ao banco de dados SQLite.');
|
||||
initializeDatabase();
|
||||
}
|
||||
});
|
||||
|
||||
// Função para inicializar as tabelas
|
||||
function initializeDatabase() {
|
||||
// Tabela de produtos
|
||||
db.run(`CREATE TABLE IF NOT EXISTS produtos (
|
||||
id TEXT PRIMARY KEY,
|
||||
id_produto TEXT,
|
||||
marca TEXT NOT NULL,
|
||||
nome TEXT NOT NULL,
|
||||
estacao TEXT NOT NULL,
|
||||
genero TEXT DEFAULT 'Unissex',
|
||||
fornecedor_id TEXT,
|
||||
valor_compra REAL NOT NULL,
|
||||
valor_revenda REAL NOT NULL,
|
||||
foto_principal_url TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (fornecedor_id) REFERENCES fornecedores (id)
|
||||
)`);
|
||||
|
||||
// Tabela de variações de produtos (tamanho, cor, quantidade)
|
||||
db.run(`CREATE TABLE IF NOT EXISTS produto_variacoes (
|
||||
id TEXT PRIMARY KEY,
|
||||
produto_id TEXT NOT NULL,
|
||||
tamanho TEXT NOT NULL,
|
||||
cor TEXT NOT NULL,
|
||||
quantidade INTEGER NOT NULL DEFAULT 0,
|
||||
foto_url TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (produto_id) REFERENCES produtos (id) ON DELETE CASCADE
|
||||
)`);
|
||||
|
||||
// Tabela de clientes
|
||||
db.run(`CREATE TABLE IF NOT EXISTS clientes (
|
||||
id TEXT PRIMARY KEY,
|
||||
nome_completo TEXT NOT NULL,
|
||||
email TEXT,
|
||||
telefone TEXT,
|
||||
endereco TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`);
|
||||
|
||||
// Tabela de fornecedores
|
||||
db.run(`CREATE TABLE IF NOT EXISTS fornecedores (
|
||||
id TEXT PRIMARY KEY,
|
||||
razao_social TEXT NOT NULL,
|
||||
telefone TEXT,
|
||||
whatsapp TEXT,
|
||||
endereco TEXT,
|
||||
email TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`);
|
||||
|
||||
// Tabela de tipos de despesas
|
||||
db.run(`CREATE TABLE IF NOT EXISTS tipos_despesas (
|
||||
id TEXT PRIMARY KEY,
|
||||
nome TEXT NOT NULL UNIQUE,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`);
|
||||
|
||||
// Tabela de despesas
|
||||
db.run(`CREATE TABLE IF NOT EXISTS despesas (
|
||||
id TEXT PRIMARY KEY,
|
||||
tipo_despesa_id TEXT NOT NULL,
|
||||
fornecedor_id TEXT,
|
||||
data DATE NOT NULL,
|
||||
valor REAL NOT NULL,
|
||||
descricao TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (tipo_despesa_id) REFERENCES tipos_despesas (id),
|
||||
FOREIGN KEY (fornecedor_id) REFERENCES fornecedores (id)
|
||||
)`);
|
||||
|
||||
// Tabela de vendas
|
||||
db.run(`CREATE TABLE IF NOT EXISTS vendas (
|
||||
id TEXT PRIMARY KEY,
|
||||
cliente_id TEXT,
|
||||
tipo_pagamento TEXT NOT NULL, -- 'vista' ou 'parcelado'
|
||||
valor_total REAL NOT NULL,
|
||||
desconto REAL DEFAULT 0,
|
||||
parcelas INTEGER DEFAULT 1,
|
||||
valor_parcela REAL,
|
||||
data_venda DATE NOT NULL,
|
||||
status TEXT DEFAULT 'concluida', -- 'concluida', 'cancelada'
|
||||
observacoes TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (cliente_id) REFERENCES clientes (id)
|
||||
)`);
|
||||
|
||||
// Tabela de itens da venda
|
||||
db.run(`CREATE TABLE IF NOT EXISTS venda_itens (
|
||||
id TEXT PRIMARY KEY,
|
||||
venda_id TEXT NOT NULL,
|
||||
produto_variacao_id TEXT NOT NULL,
|
||||
quantidade INTEGER NOT NULL,
|
||||
valor_unitario REAL NOT NULL,
|
||||
valor_total REAL NOT NULL,
|
||||
FOREIGN KEY (venda_id) REFERENCES vendas (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (produto_variacao_id) REFERENCES produto_variacoes (id)
|
||||
)`);
|
||||
|
||||
console.log('Tabelas do banco de dados inicializadas.');
|
||||
}
|
||||
console.log('🚀 Servidor configurado para usar Supabase!')
|
||||
console.log('📊 Banco de dados:', supabaseUrl)
|
||||
|
||||
// Rotas da API
|
||||
|
||||
// === PRODUTOS ===
|
||||
app.get('/api/produtos', (req, res) => {
|
||||
const query = `
|
||||
SELECT p.*, f.razao_social as fornecedor_nome,
|
||||
COUNT(pv.id) as total_variacoes,
|
||||
SUM(pv.quantidade) as estoque_total
|
||||
FROM produtos p
|
||||
LEFT JOIN fornecedores f ON p.fornecedor_id = f.id
|
||||
LEFT JOIN produto_variacoes pv ON p.id = pv.produto_id
|
||||
GROUP BY p.id
|
||||
ORDER BY p.created_at DESC
|
||||
`;
|
||||
|
||||
db.all(query, [], (err, rows) => {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
res.json(rows);
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/api/produtos', upload.any(), (req, res) => {
|
||||
console.log('Recebendo requisição para criar produto:', req.body);
|
||||
console.log('Arquivos recebidos:', req.files ? req.files.length : 0);
|
||||
|
||||
const { id_produto, marca, nome, estacao, genero, fornecedor_id, valor_compra, valor_revenda, variacoes_data } = req.body;
|
||||
const produtoId = uuidv4();
|
||||
|
||||
// Validações básicas
|
||||
if (!marca || !nome || !estacao || !valor_compra || !valor_revenda) {
|
||||
return res.status(400).json({ error: 'Campos obrigatórios não preenchidos' });
|
||||
}
|
||||
|
||||
// Parse das variações
|
||||
let variacoes = [];
|
||||
app.get('/api/produtos', async (req, res) => {
|
||||
try {
|
||||
if (variacoes_data) {
|
||||
variacoes = JSON.parse(variacoes_data);
|
||||
}
|
||||
const { data: produtos, error } = await supabase
|
||||
.from('produtos')
|
||||
.select(`
|
||||
*,
|
||||
fornecedores(nome),
|
||||
produto_variacoes(id, quantidade)
|
||||
`)
|
||||
.order('created_at', { ascending: false })
|
||||
|
||||
if (error) throw error
|
||||
|
||||
// Calcular estatísticas
|
||||
const produtosComEstatisticas = produtos.map(produto => ({
|
||||
...produto,
|
||||
fornecedor_nome: produto.fornecedores?.nome || null,
|
||||
total_variacoes: produto.produto_variacoes?.length || 0,
|
||||
estoque_total: produto.produto_variacoes?.reduce((total, variacao) => total + (variacao.quantidade || 0), 0) || 0
|
||||
}))
|
||||
|
||||
res.json(produtosComEstatisticas)
|
||||
} catch (error) {
|
||||
console.error('Erro ao fazer parse das variações:', error);
|
||||
return res.status(400).json({ error: 'Dados de variações inválidos' });
|
||||
console.error('Erro ao buscar produtos:', error)
|
||||
res.status(500).json({ error: error.message })
|
||||
}
|
||||
|
||||
if (variacoes.length === 0) {
|
||||
return res.status(400).json({ error: 'Pelo menos uma variação é obrigatória' });
|
||||
}
|
||||
|
||||
// Iniciar transação
|
||||
db.serialize(() => {
|
||||
db.run('BEGIN TRANSACTION');
|
||||
|
||||
// Inserir produto
|
||||
const produtoQuery = `INSERT INTO produtos (id, id_produto, marca, nome, estacao, genero, fornecedor_id, valor_compra, valor_revenda)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
||||
|
||||
const fornecedorIdFinal = fornecedor_id && fornecedor_id !== '' ? fornecedor_id : null;
|
||||
|
||||
db.run(produtoQuery, [produtoId, id_produto, marca, nome, estacao, genero || 'Unissex', fornecedorIdFinal, valor_compra, valor_revenda], function(err) {
|
||||
if (err) {
|
||||
console.error('Erro ao inserir produto:', err);
|
||||
db.run('ROLLBACK');
|
||||
return res.status(500).json({ error: err.message });
|
||||
}
|
||||
|
||||
console.log('Produto inserido com sucesso, processando variações...');
|
||||
|
||||
// Processar variações e fotos
|
||||
let variacoesProcessadas = 0;
|
||||
const totalVariacoes = variacoes.length;
|
||||
|
||||
if (totalVariacoes === 0) {
|
||||
db.run('COMMIT');
|
||||
return res.json({ id: produtoId, message: 'Produto criado com sucesso!' });
|
||||
}
|
||||
|
||||
variacoes.forEach((variacao, varIndex) => {
|
||||
const variacaoId = uuidv4();
|
||||
|
||||
// Inserir variação
|
||||
const variacaoQuery = `INSERT INTO produto_variacoes (id, produto_id, tamanho, cor, quantidade)
|
||||
VALUES (?, ?, ?, ?, ?)`;
|
||||
|
||||
db.run(variacaoQuery, [variacaoId, produtoId, variacao.tamanho, variacao.cor, variacao.quantidade], function(err) {
|
||||
if (err) {
|
||||
console.error('Erro ao inserir variação:', err);
|
||||
db.run('ROLLBACK');
|
||||
return res.status(500).json({ error: err.message });
|
||||
}
|
||||
|
||||
// Processar fotos desta variação
|
||||
const fotosVariacao = req.files ? req.files.filter(file =>
|
||||
file.fieldname.startsWith(`variacao_${varIndex}_foto_`)
|
||||
) : [];
|
||||
|
||||
if (fotosVariacao.length > 0) {
|
||||
// Usar a primeira foto como foto_url da variação
|
||||
const primeiraFoto = fotosVariacao[0];
|
||||
const fotoUrl = `/uploads/${primeiraFoto.filename}`;
|
||||
|
||||
// Atualizar variação com a primeira foto
|
||||
db.run('UPDATE produto_variacoes SET foto_url = ? WHERE id = ?', [fotoUrl, variacaoId], function(err) {
|
||||
if (err) {
|
||||
console.error('Erro ao atualizar foto da variação:', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
variacoesProcessadas++;
|
||||
console.log(`Variação processada: ${variacoesProcessadas}/${totalVariacoes}`);
|
||||
|
||||
if (variacoesProcessadas === totalVariacoes) {
|
||||
db.run('COMMIT');
|
||||
console.log('Produto criado com sucesso!');
|
||||
res.json({ id: produtoId, message: 'Produto criado com sucesso!' });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.put('/api/produtos/:id', upload.single('foto_principal'), (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { id_produto, marca, nome, estacao, genero, fornecedor_id, valor_compra, valor_revenda } = req.body;
|
||||
|
||||
let query, params;
|
||||
|
||||
if (req.file) {
|
||||
const foto_principal_url = `/uploads/${req.file.filename}`;
|
||||
query = `UPDATE produtos
|
||||
SET id_produto = ?, marca = ?, nome = ?, estacao = ?, genero = ?, fornecedor_id = ?, valor_compra = ?, valor_revenda = ?, foto_principal_url = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?`;
|
||||
params = [id_produto, marca, nome, estacao, genero || 'Unissex', fornecedor_id, valor_compra, valor_revenda, foto_principal_url, id];
|
||||
} else {
|
||||
query = `UPDATE produtos
|
||||
SET id_produto = ?, marca = ?, nome = ?, estacao = ?, genero = ?, fornecedor_id = ?, valor_compra = ?, valor_revenda = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?`;
|
||||
params = [id_produto, marca, nome, estacao, genero || 'Unissex', fornecedor_id, valor_compra, valor_revenda, id];
|
||||
}
|
||||
|
||||
db.run(query, params, function(err) {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
app.post('/api/produtos', upload.any(), async (req, res) => {
|
||||
try {
|
||||
console.log('Recebendo requisição para criar produto:', req.body);
|
||||
console.log('Arquivos recebidos:', req.files ? req.files.length : 0);
|
||||
|
||||
const { id_produto, marca, nome, estacao, genero, fornecedor_id, valor_compra, valor_revenda, variacoes_data } = req.body;
|
||||
|
||||
// Validações básicas
|
||||
if (!marca || !nome || !estacao || !valor_compra || !valor_revenda) {
|
||||
return res.status(400).json({ error: 'Campos obrigatórios não preenchidos' });
|
||||
}
|
||||
res.json({ message: 'Produto atualizado com sucesso!' });
|
||||
});
|
||||
|
||||
// Parse das variações
|
||||
let variacoes = [];
|
||||
try {
|
||||
if (variacoes_data) {
|
||||
variacoes = JSON.parse(variacoes_data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erro ao fazer parse das variações:', error);
|
||||
return res.status(400).json({ error: 'Dados de variações inválidos' });
|
||||
}
|
||||
|
||||
if (variacoes.length === 0) {
|
||||
return res.status(400).json({ error: 'Pelo menos uma variação é obrigatória' });
|
||||
}
|
||||
|
||||
// Inserir produto no Supabase
|
||||
const { data: produto, error: produtoError } = await supabase
|
||||
.from('produtos')
|
||||
.insert([{
|
||||
id_produto,
|
||||
marca,
|
||||
nome,
|
||||
estacao,
|
||||
genero: genero || 'Unissex',
|
||||
fornecedor_id: fornecedor_id || null,
|
||||
valor_compra: parseFloat(valor_compra),
|
||||
valor_revenda: parseFloat(valor_revenda)
|
||||
}])
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (produtoError) throw produtoError
|
||||
|
||||
console.log('Produto inserido com sucesso, processando variações...');
|
||||
|
||||
// Inserir variações
|
||||
const variacoesParaInserir = variacoes.map(variacao => ({
|
||||
produto_id: produto.id,
|
||||
tamanho: variacao.tamanho,
|
||||
cor: variacao.cor,
|
||||
quantidade: parseInt(variacao.quantidade)
|
||||
}))
|
||||
|
||||
const { error: variacoesError } = await supabase
|
||||
.from('produto_variacoes')
|
||||
.insert(variacoesParaInserir)
|
||||
|
||||
if (variacoesError) throw variacoesError
|
||||
|
||||
console.log('Produto criado com sucesso!');
|
||||
res.json({ id: produto.id, message: 'Produto criado com sucesso!' });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar produto:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// === VARIAÇÕES DE PRODUTOS ===
|
||||
app.get('/api/produtos/:id/variacoes', (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
db.all('SELECT * FROM produto_variacoes WHERE produto_id = ? ORDER BY tamanho, cor', [id], (err, rows) => {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
res.json(rows);
|
||||
});
|
||||
app.get('/api/produtos/:id/variacoes', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const { data: variacoes, error } = await supabase
|
||||
.from('produto_variacoes')
|
||||
.select('*')
|
||||
.eq('produto_id', id)
|
||||
.order('tamanho')
|
||||
.order('cor')
|
||||
|
||||
if (error) throw error
|
||||
res.json(variacoes)
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar variações:', error)
|
||||
res.status(500).json({ error: error.message })
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/produtos/:id/variacoes', upload.single('foto'), (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { tamanho, cor, quantidade } = req.body;
|
||||
const foto_url = req.file ? `/uploads/${req.file.filename}` : null;
|
||||
const variacao_id = uuidv4();
|
||||
|
||||
const query = `INSERT INTO produto_variacoes (id, produto_id, tamanho, cor, quantidade, foto_url)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`;
|
||||
|
||||
db.run(query, [variacao_id, id, tamanho, cor, quantidade, foto_url], function(err) {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
res.json({ id: variacao_id, message: 'Variação adicionada com sucesso!' });
|
||||
});
|
||||
app.post('/api/produtos/:id/variacoes', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { tamanho, cor, quantidade } = req.body;
|
||||
|
||||
const { data: variacao, error } = await supabase
|
||||
.from('produto_variacoes')
|
||||
.insert([{
|
||||
produto_id: id,
|
||||
tamanho,
|
||||
cor,
|
||||
quantidade: parseInt(quantidade)
|
||||
}])
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw error
|
||||
res.json({ id: variacao.id, message: 'Variação adicionada com sucesso!' })
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar variação:', error)
|
||||
res.status(500).json({ error: error.message })
|
||||
}
|
||||
});
|
||||
|
||||
// === CLIENTES ===
|
||||
app.get('/api/clientes', (req, res) => {
|
||||
db.all('SELECT * FROM clientes ORDER BY nome_completo', [], (err, rows) => {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
res.json(rows);
|
||||
});
|
||||
app.get('/api/clientes', async (req, res) => {
|
||||
try {
|
||||
const { data: clientes, error } = await supabase
|
||||
.from('clientes')
|
||||
.select('*')
|
||||
.order('nome_completo')
|
||||
|
||||
if (error) throw error
|
||||
res.json(clientes)
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar clientes:', error)
|
||||
res.status(500).json({ error: error.message })
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/clientes', (req, res) => {
|
||||
const { nome_completo, email, telefone, endereco } = req.body;
|
||||
const id = uuidv4();
|
||||
|
||||
const query = `INSERT INTO clientes (id, nome_completo, email, telefone, endereco)
|
||||
VALUES (?, ?, ?, ?, ?)`;
|
||||
|
||||
db.run(query, [id, nome_completo, email, telefone, endereco], function(err) {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
res.json({ id, message: 'Cliente cadastrado com sucesso!' });
|
||||
});
|
||||
app.post('/api/clientes', async (req, res) => {
|
||||
try {
|
||||
const { nome_completo, email, whatsapp, endereco } = req.body;
|
||||
|
||||
const { data: cliente, error } = await supabase
|
||||
.from('clientes')
|
||||
.insert([{
|
||||
nome_completo,
|
||||
email: email || null,
|
||||
whatsapp,
|
||||
endereco
|
||||
}])
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw error
|
||||
res.json({ id: cliente.id, message: 'Cliente cadastrado com sucesso!' })
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar cliente:', error)
|
||||
res.status(500).json({ error: error.message })
|
||||
}
|
||||
});
|
||||
|
||||
// === FORNECEDORES ===
|
||||
app.get('/api/fornecedores', (req, res) => {
|
||||
db.all('SELECT * FROM fornecedores ORDER BY razao_social', [], (err, rows) => {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
res.json(rows);
|
||||
});
|
||||
app.get('/api/fornecedores', async (req, res) => {
|
||||
try {
|
||||
const { data: fornecedores, error } = await supabase
|
||||
.from('fornecedores')
|
||||
.select('*')
|
||||
.order('nome')
|
||||
|
||||
if (error) throw error
|
||||
res.json(fornecedores)
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar fornecedores:', error)
|
||||
res.status(500).json({ error: error.message })
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/fornecedores', (req, res) => {
|
||||
const { razao_social, telefone, whatsapp, endereco, email } = req.body;
|
||||
const id = uuidv4();
|
||||
|
||||
const query = `INSERT INTO fornecedores (id, razao_social, telefone, whatsapp, endereco, email)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`;
|
||||
|
||||
db.run(query, [id, razao_social, telefone, whatsapp, endereco, email], function(err) {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
res.json({ id, message: 'Fornecedor cadastrado com sucesso!' });
|
||||
});
|
||||
app.post('/api/fornecedores', async (req, res) => {
|
||||
try {
|
||||
const { nome, cnpj, telefone, email, endereco } = req.body;
|
||||
|
||||
const { data: fornecedor, error } = await supabase
|
||||
.from('fornecedores')
|
||||
.insert([{
|
||||
nome,
|
||||
cnpj: cnpj || null,
|
||||
telefone: telefone || null,
|
||||
email: email || null,
|
||||
endereco: endereco || null
|
||||
}])
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw error
|
||||
res.json({ id: fornecedor.id, message: 'Fornecedor cadastrado com sucesso!' })
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar fornecedor:', error)
|
||||
res.status(500).json({ error: error.message })
|
||||
}
|
||||
});
|
||||
|
||||
// === TIPOS DE DESPESAS ===
|
||||
app.get('/api/tipos-despesas', (req, res) => {
|
||||
db.all('SELECT * FROM tipos_despesas ORDER BY nome', [], (err, rows) => {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
res.json(rows);
|
||||
});
|
||||
app.get('/api/tipos-despesas', async (req, res) => {
|
||||
try {
|
||||
const { data: tipos, error } = await supabase
|
||||
.from('tipos_despesa')
|
||||
.select('*')
|
||||
.order('nome')
|
||||
|
||||
if (error) throw error
|
||||
res.json(tipos)
|
||||
} catch (error) {
|
||||
console.error('Erro ao buscar tipos de despesa:', error)
|
||||
res.status(500).json({ error: error.message })
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/tipos-despesas', (req, res) => {
|
||||
const { nome } = req.body;
|
||||
const id = uuidv4();
|
||||
|
||||
const query = `INSERT INTO tipos_despesas (id, nome) VALUES (?, ?)`;
|
||||
|
||||
db.run(query, [id, nome], function(err) {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
res.json({ id, message: 'Tipo de despesa criado com sucesso!' });
|
||||
});
|
||||
});
|
||||
|
||||
// === DESPESAS ===
|
||||
app.get('/api/despesas', (req, res) => {
|
||||
const query = `
|
||||
SELECT d.*, td.nome as tipo_nome, f.razao_social as fornecedor_nome
|
||||
FROM despesas d
|
||||
LEFT JOIN tipos_despesas td ON d.tipo_despesa_id = td.id
|
||||
LEFT JOIN fornecedores f ON d.fornecedor_id = f.id
|
||||
ORDER BY d.data DESC
|
||||
`;
|
||||
|
||||
db.all(query, [], (err, rows) => {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
res.json(rows);
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/api/despesas', (req, res) => {
|
||||
const { tipo_despesa_id, fornecedor_id, data, valor, descricao } = req.body;
|
||||
const id = uuidv4();
|
||||
|
||||
const query = `INSERT INTO despesas (id, tipo_despesa_id, fornecedor_id, data, valor, descricao)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`;
|
||||
|
||||
db.run(query, [id, tipo_despesa_id, fornecedor_id || null, data, valor, descricao], function(err) {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
res.json({ id, message: 'Despesa cadastrada com sucesso!' });
|
||||
});
|
||||
});
|
||||
|
||||
app.put('/api/despesas/:id', (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { tipo_despesa_id, fornecedor_id, data, valor, descricao } = req.body;
|
||||
|
||||
const query = `UPDATE despesas
|
||||
SET tipo_despesa_id = ?, fornecedor_id = ?, data = ?, valor = ?, descricao = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?`;
|
||||
|
||||
db.run(query, [tipo_despesa_id, fornecedor_id || null, data, valor, descricao, id], function(err) {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
res.json({ message: 'Despesa atualizada com sucesso!' });
|
||||
});
|
||||
});
|
||||
|
||||
app.delete('/api/despesas/:id', (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
db.run('DELETE FROM despesas WHERE id = ?', [id], function(err) {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
res.json({ message: 'Despesa excluída com sucesso!' });
|
||||
});
|
||||
});
|
||||
|
||||
// === VENDAS ===
|
||||
app.get('/api/vendas', (req, res) => {
|
||||
const query = `
|
||||
SELECT v.*, c.nome_completo as cliente_nome
|
||||
FROM vendas v
|
||||
LEFT JOIN clientes c ON v.cliente_id = c.id
|
||||
ORDER BY v.data_venda DESC
|
||||
`;
|
||||
|
||||
db.all(query, [], (err, rows) => {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
res.json(rows);
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/api/vendas', (req, res) => {
|
||||
const { cliente_id, tipo_pagamento, valor_total, desconto, parcelas, data_venda, observacoes, itens } = req.body;
|
||||
const vendaId = uuidv4();
|
||||
const valor_parcela = tipo_pagamento === 'parcelado' ? (valor_total - desconto) / parcelas : 0;
|
||||
|
||||
// Iniciar transação
|
||||
db.serialize(() => {
|
||||
db.run('BEGIN TRANSACTION');
|
||||
app.post('/api/tipos-despesas', async (req, res) => {
|
||||
try {
|
||||
const { nome } = req.body;
|
||||
|
||||
// Inserir venda
|
||||
const vendaQuery = `INSERT INTO vendas (id, cliente_id, tipo_pagamento, valor_total, desconto, parcelas, valor_parcela, data_venda, observacoes)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
||||
|
||||
db.run(vendaQuery, [vendaId, cliente_id || null, tipo_pagamento, valor_total, desconto, parcelas, valor_parcela, data_venda, observacoes], function(err) {
|
||||
if (err) {
|
||||
db.run('ROLLBACK');
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
|
||||
// Inserir itens da venda
|
||||
let itemsProcessed = 0;
|
||||
const totalItems = itens.length;
|
||||
|
||||
if (totalItems === 0) {
|
||||
db.run('COMMIT');
|
||||
res.json({ id: vendaId, message: 'Venda registrada com sucesso!' });
|
||||
return;
|
||||
}
|
||||
|
||||
itens.forEach((item) => {
|
||||
const itemId = uuidv4();
|
||||
const itemQuery = `INSERT INTO venda_itens (id, venda_id, produto_variacao_id, quantidade, valor_unitario, valor_total)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`;
|
||||
|
||||
db.run(itemQuery, [itemId, vendaId, item.produto_variacao_id, item.quantidade, item.valor_unitario, item.valor_total], function(err) {
|
||||
if (err) {
|
||||
db.run('ROLLBACK');
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
|
||||
// Atualizar estoque
|
||||
const updateEstoque = `UPDATE produto_variacoes
|
||||
SET quantidade = quantidade - ?
|
||||
WHERE id = ?`;
|
||||
|
||||
db.run(updateEstoque, [item.quantidade, item.produto_variacao_id], function(err) {
|
||||
if (err) {
|
||||
db.run('ROLLBACK');
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
|
||||
itemsProcessed++;
|
||||
if (itemsProcessed === totalItems) {
|
||||
db.run('COMMIT');
|
||||
res.json({ id: vendaId, message: 'Venda registrada com sucesso!' });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
const { data: tipo, error } = await supabase
|
||||
.from('tipos_despesa')
|
||||
.insert([{ nome }])
|
||||
.select()
|
||||
.single()
|
||||
|
||||
app.delete('/api/vendas/:id', (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
db.run('DELETE FROM vendas WHERE id = ?', [id], function(err) {
|
||||
if (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
return;
|
||||
}
|
||||
res.json({ message: 'Venda excluída com sucesso!' });
|
||||
});
|
||||
if (error) throw error
|
||||
res.json({ id: tipo.id, message: 'Tipo de despesa criado com sucesso!' })
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar tipo de despesa:', error)
|
||||
res.status(500).json({ error: error.message })
|
||||
}
|
||||
});
|
||||
|
||||
// === TESTE ===
|
||||
app.get('/api/test', (req, res) => {
|
||||
res.json({ message: 'API funcionando corretamente!', timestamp: new Date() });
|
||||
res.json({
|
||||
message: 'API funcionando com Supabase!',
|
||||
timestamp: new Date(),
|
||||
database: 'Supabase'
|
||||
});
|
||||
});
|
||||
|
||||
// === DASHBOARD ===
|
||||
app.get('/api/dashboard', (req, res) => {
|
||||
const queries = {
|
||||
totalProdutos: 'SELECT COUNT(*) as count FROM produtos',
|
||||
totalClientes: 'SELECT COUNT(*) as count FROM clientes',
|
||||
totalFornecedores: 'SELECT COUNT(*) as count FROM fornecedores',
|
||||
vendasMes: `SELECT COUNT(*) as count, SUM(valor_total) as total
|
||||
FROM vendas
|
||||
WHERE strftime('%Y-%m', data_venda) = strftime('%Y-%m', 'now')`,
|
||||
estoqueTotal: 'SELECT SUM(quantidade) as total FROM produto_variacoes',
|
||||
despesasMes: `SELECT SUM(valor) as total
|
||||
FROM despesas
|
||||
WHERE strftime('%Y-%m', data) = strftime('%Y-%m', 'now')`
|
||||
};
|
||||
app.get('/api/dashboard', async (req, res) => {
|
||||
try {
|
||||
const [
|
||||
{ count: totalProdutos },
|
||||
{ count: totalClientes },
|
||||
{ count: totalFornecedores }
|
||||
] = await Promise.all([
|
||||
supabase.from('produtos').select('*', { count: 'exact', head: true }),
|
||||
supabase.from('clientes').select('*', { count: 'exact', head: true }),
|
||||
supabase.from('fornecedores').select('*', { count: 'exact', head: true })
|
||||
])
|
||||
|
||||
const results = {};
|
||||
let completed = 0;
|
||||
const total = Object.keys(queries).length;
|
||||
|
||||
Object.entries(queries).forEach(([key, query]) => {
|
||||
db.get(query, [], (err, row) => {
|
||||
if (err) {
|
||||
results[key] = { error: err.message };
|
||||
} else {
|
||||
results[key] = row;
|
||||
}
|
||||
|
||||
completed++;
|
||||
if (completed === total) {
|
||||
res.json(results);
|
||||
}
|
||||
});
|
||||
});
|
||||
res.json({
|
||||
totalProdutos: { count: totalProdutos },
|
||||
totalClientes: { count: totalClientes },
|
||||
totalFornecedores: { count: totalFornecedores },
|
||||
vendasMes: { count: 0, total: 0 },
|
||||
estoqueTotal: { total: 0 },
|
||||
despesasMes: { total: 0 }
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Erro no dashboard:', error)
|
||||
res.status(500).json({ error: error.message })
|
||||
}
|
||||
});
|
||||
|
||||
// Servir arquivos estáticos do React
|
||||
|
||||
Reference in New Issue
Block a user