import * as SQLite from "expo-sqlite";
import { Alert } from "react-native";
let db = null;
const DB_NAME = "alqualis";
export const openDatabase = async () => {
if (!db) {
db = await SQLite.openDatabaseAsync(DB_NAME);
}
return db;
};
/**
* @description Remove o arquivo do banco (apenas para testes/development)
*/
export const deleteDatabase = async () => {
try {
// Fecha se já estiver aberta
if (db) {
await db.closeAsync();
db = null;
}
// Apaga o arquivo .db
await SQLite.deleteDatabaseAsync(DB_NAME);
Alert.alert("Sucesso", "🗑️ Banco de dados apagado com sucesso!");
} catch (error) {
//console.error("❌ Erro ao apagar banco:", error);
Alert.alert("Erro", "Não foi possível apagar o banco de dados.");
}
};
/**
* @description Cria as tabelas iniciais
*/
export const createDatabase = async (mensagem = true) => {
const database = await openDatabase();
try {
await database.execAsync(`
PRAGMA foreign_keys = ON;
-- Tabela produtor
CREATE TABLE IF NOT EXISTS produtor (
id_produtor INTEGER PRIMARY KEY,
nome_produtor TEXT NOT NULL,
cpf_produtor TEXT,
codigo_produtor TEXT,
UNIQUE (cpf_produtor),
UNIQUE (codigo_produtor)
);
-- Tabela comunidade
CREATE TABLE IF NOT EXISTS comunidade (
id_comunidade INTEGER PRIMARY KEY,
nome_comunidade TEXT,
UNIQUE (nome_comunidade)
);
-- Tabela municipio
CREATE TABLE IF NOT EXISTS municipio (
id_municipio INTEGER PRIMARY KEY,
nome_municipio TEXT,
UNIQUE (nome_municipio)
);
-- Tabela cooperativa
CREATE TABLE IF NOT EXISTS cooperativa (
id_cooperativa INTEGER PRIMARY KEY,
nome_cooperativa TEXT,
UNIQUE (nome_cooperativa)
);
-- Tabela variedade
CREATE TABLE IF NOT EXISTS variedade (
id_variedade INTEGER PRIMARY KEY,
nome_variedade TEXT,
UNIQUE (nome_variedade)
);
-- Tabela plantacao
CREATE TABLE IF NOT EXISTS plantacao (
id_plantacao INTEGER PRIMARY KEY,
id_produtor INTEGER NOT NULL,
id_variedade INTEGER NOT NULL,
id_comunidade INTEGER NOT NULL,
id_municipio INTEGER NOT NULL,
nome_plantacao TEXT NOT NULL,
latitude TEXT,
longitude TEXT,
altitude_media TEXT,
nome_talhao TEXT,
face_exposicao TEXT,
meses_colheita TEXT,
FOREIGN KEY (id_produtor) REFERENCES produtor(id_produtor),
FOREIGN KEY (id_variedade) REFERENCES variedade(id_variedade),
FOREIGN KEY (id_comunidade) REFERENCES comunidade(id_comunidade),
FOREIGN KEY (id_municipio) REFERENCES municipio(id_municipio)
);
-- Índices auxiliares
CREATE INDEX IF NOT EXISTS idx_plantacao_produtor ON plantacao(id_produtor);
CREATE INDEX IF NOT EXISTS idx_plantacao_variedade ON plantacao(id_variedade);
CREATE INDEX IF NOT EXISTS idx_plantacao_comunidade ON plantacao(id_comunidade);
CREATE INDEX IF NOT EXISTS idx_plantacao_municipio ON plantacao(id_municipio);
-- Tabela cooperativa_produtor
CREATE TABLE IF NOT EXISTS cooperativa_produtor (
id_cooperativa_produtor INTEGER PRIMARY KEY,
id_cooperativa INTEGER NOT NULL,
id_produtor INTEGER NOT NULL,
FOREIGN KEY (id_cooperativa) REFERENCES cooperativa(id_cooperativa),
FOREIGN KEY (id_produtor) REFERENCES produtor(id_produtor)
);
CREATE INDEX IF NOT EXISTS idx_cp_cooperativa ON cooperativa_produtor(id_cooperativa);
CREATE INDEX IF NOT EXISTS idx_cp_produtor ON cooperativa_produtor(id_produtor);
-- Tabela face_exposicao
CREATE TABLE IF NOT EXISTS face_exposicao (
id_face_exposicao INTEGER PRIMARY KEY,
nome_face_exposicao TEXT,
UNIQUE (nome_face_exposicao)
);
-- Tabela de junção face_exposicao_plantacao
CREATE TABLE IF NOT EXISTS face_exposicao_plantacao (
id_face_exposicao_plantacao INTEGER PRIMARY KEY,
id_face_exposicao INTEGER,
id_plantacao INTEGER,
FOREIGN KEY (id_face_exposicao) REFERENCES face_exposicao(id_face_exposicao),
FOREIGN KEY (id_plantacao) REFERENCES plantacao(id_plantacao)
);
CREATE INDEX IF NOT EXISTS idx_fep_face ON face_exposicao_plantacao(id_face_exposicao);
CREATE INDEX IF NOT EXISTS idx_fep_plantacao ON face_exposicao_plantacao(id_plantacao);
`);
mensagem && Alert.alert("Sucesso", "Banco de dados criado com sucesso!");
return database;
} catch (error) {
//console.error("❌ Erro ao criar banco de dados:", error);
mensagem && Alert.alert("Erro", `Erro ao criar o banco de dados.\n${error}`);
throw error;
}
};
/**
* @description Insere um produtor e opcionalmente associa a cooperativa
* @param {*} nome_produtor
* @param {*} cpf_produtor
* @param {*} codigo_produtor
* @param {*} id_cooperativa
*/
export const inserirProdutor = async ({
nome_produtor,
cpf_produtor = "",
codigo_produtor = null,
id_cooperativa = null,
mensagem = true,
}) => {
if (!nome_produtor && mensagem) {
Alert.alert("Erro", "O nome do produtor é obrigatório.");
return null;
}
const database = await openDatabase();
try {
// 🔍 Verifica se CPF já existe (se fornecido)
if (cpf_produtor) {
const existeCPF = await database.getFirstAsync(
`SELECT 1 FROM produtor WHERE cpf_produtor = ? LIMIT 1;`,
[cpf_produtor]
);
if (existeCPF) {
mensagem && Alert.alert("Erro", "Este CPF já está cadastrado.")
return null;
}
}
// 🔍 Verifica se Código já existe (se fornecido)
if (codigo_produtor) {
const existeCodigo = await database.getFirstAsync(
`SELECT 1 FROM produtor WHERE codigo_produtor = ? LIMIT 1;`,
[codigo_produtor]
);
if (existeCodigo) {
Alert.alert("Erro", "Este código de produtor já está cadastrado.");
return null;
}
}
// ✅ Insere produtor
const result = await database.runAsync(
`INSERT INTO produtor (nome_produtor, cpf_produtor, codigo_produtor)
VALUES (?, ?, ?);`,
[nome_produtor.toUpperCase(), cpf_produtor, codigo_produtor.replace(/\s+/g, '')]
);
const id = result.lastInsertRowId;
// 🔗 Associa cooperativa (se houver)
if (id_cooperativa) {
await database.runAsync(
`INSERT INTO cooperativa_produtor (id_cooperativa, id_produtor)
VALUES (?, ?);`,
[id_cooperativa, id]
);
}
mensagem && Alert.alert("Sucesso", "Produtor cadastrado com sucesso!")
return id;
} catch (error) {
//console.error("❌ Erro ao inserir produtor:", error);
mensagem && Alert.alert("Erro", `Erro ao cadastrar o produtor.\n${error}`)
return null;
}
};
/**
* @description Associa produtor ↔ cooperativa
* @param {*} id_produtor
* @param {*} id_cooperativa
* @returns {boolean} true or false
*/
export const associarProdutorCooperativa = async ({
id_produtor,
id_cooperativa,
mensagem = true
}) => {
if (!id_produtor || !id_cooperativa) {
mensagem && Alert.alert("Erro", "Produtor e Cooperativa devem ser informados.")
return false;
}
const database = await openDatabase();
try {
await database.runAsync(
`INSERT INTO cooperativa_produtor (id_cooperativa, id_produtor)
VALUES (?, ?);`,
[id_cooperativa, id_produtor]
);
mensagem && Alert.alert("Sucesso", "Associação realizada com sucesso!")
return true;
} catch (error) {
//console.error("❌ Erro ao associar produtor à cooperativa:", error);
mensagem && Alert.alert("Erro", `Erro ao associar produtor à cooperativa.\n${error}`)
return false;
}
};
/**
* @description Atualiza um produtor e sua associação com cooperativa
* @param {*} id_produtor
* @param {*} nome_produtor
* @param {*} cpf_produtor
* @param {*} codigo_produtor
* @param {*} id_cooperativa
* @returns {boolean} true se sucesso, false se erro
*/
export const atualizarProdutor = async ({
id_produtor,
nome_produtor,
cpf_produtor = "",
codigo_produtor = null,
id_cooperativa = null,
mensagem = true
}) => {
if (!id_produtor || !nome_produtor) {
mensagem && Alert.alert("Erro", "ID e nome do produtor são obrigatórios.")
return false;
}
const database = await openDatabase();
try {
// 🔍 Verifica se CPF já existe para outro produtor
if (cpf_produtor) {
const cpfDuplicado = await database.getFirstAsync(
`SELECT 1 FROM produtor WHERE cpf_produtor = ? AND id_produtor != ? LIMIT 1;`,
[cpf_produtor, id_produtor]
);
if (cpfDuplicado) {
mensagem && Alert.alert("Erro", "Este CPF já está cadastrado para outro produtor.")
return false;
}
}
// 🔍 Verifica se Código já existe para outro produtor
if (codigo_produtor) {
const codigoDuplicado = await database.getFirstAsync(
`SELECT 1 FROM produtor WHERE codigo_produtor = ? AND id_produtor != ? LIMIT 1;`,
[codigo_produtor, id_produtor]
);
if (codigoDuplicado) {
mensagem && Alert.alert("Erro", "Este código já está cadastrado para outro produtor.")
return false;
}
}
// ✅ Atualiza dados do produtor
await database.runAsync(
`UPDATE produtor
SET nome_produtor = ?, cpf_produtor = ?, codigo_produtor = ?
WHERE id_produtor = ?;`,
[nome_produtor, cpf_produtor, codigo_produtor, id_produtor]
);
// 🔄 Remove associação anterior
await database.runAsync(
`DELETE FROM cooperativa_produtor WHERE id_produtor = ?;`,
[id_produtor]
);
// 🔗 Reassocia cooperativa (se fornecida)
if (id_cooperativa) {
await database.runAsync(
`INSERT INTO cooperativa_produtor (id_cooperativa, id_produtor)
VALUES (?, ?);`,
[id_cooperativa, id_produtor]
);
}
mensagem && Alert.alert("Sucesso", "Produtor atualizado com sucesso!")
return true;
} catch (error) {
//console.error("❌ Erro ao atualizar produtor:", error);
mensagem && Alert.alert("Erro", `Erro ao atualizar o produtor.\n${error}`)
return false;
}
};
/**
* @description Busca todos os registros de uma tabela
* @param {*} nomeTabela
*/
export const buscarRegistrosGenericos = async (nomeTabela) => {
const database = await openDatabase();
try {
const rows = await database.getAllAsync(
`SELECT * FROM ${nomeTabela};`
);
return rows;
} catch (error) {
//console.error(`❌ Erro ao buscar registros de ${nomeTabela}:`, error);
Alert.alert("Erro", `Não foi possível buscar ${nomeTabela}.\n${error}`);
return [];
}
};
/**
* @description Busca um único registro de uma tabela pelo ID
* @param {string} nomeTabela - Nome da tabela (ex: 'produtor')
* @param {*} id - ID do registro a ser buscado
* @returns {Promise<Object|null>} Objeto com os dados ou null se não encontrado
*/
export const buscarRegistroGenericoPorId = async (nomeTabela, id) => {
const database = await openDatabase();
try {
const row = await database.getFirstAsync(
`SELECT * FROM ${nomeTabela} WHERE id_${nomeTabela} = ?;`,
[id]
);
return row || null;
} catch (error) {
//console.error(`❌ Erro ao buscar registro em ${nomeTabela} por ID:`, error);
Alert.alert("Erro", `Não foi possível buscar o registro de ${nomeTabela}.\n${error}`);
return null;
}
};
/**
* @description Insere um registro genérico em qualquer tabela
* @param {string} table - Nome da tabela (ex: 'cooperativa')
* @param {Object} data - Objeto com um único par { coluna: valor }
* @param {string} successMessage - Texto de sucesso no Alert
* @returns {Promise<number|null>} ID inserido ou null em caso de erro
*/
export const inserirGenerico = async (table, data, successMessage='Dado inserido com sucesso!', mensagem=true) => {
const entries = Object.entries(data);
if (entries.length !== 1) {
throw new Error("inserirGenerico precisa de exatamente uma coluna/valor");
}
const [column, value] = entries[0];
const valor = value?.toString().trim().toUpperCase();
if (!valor) {
mensagem && Alert.alert("Erro", `O campo ${column} é obrigatório.`)
return null;
}
const db = await openDatabase();
try {
// Verificação se já existe
const checkSql = `SELECT 1 FROM ${table} WHERE ${column} = ? LIMIT 1;`;
const existing = await db.getFirstAsync(checkSql, [valor]);
if (existing) {
mensagem && Alert.alert("Aviso", `O valor "${valor}" já está cadastrado em ${table}.`)
return null;
}
// Inserção
const insertSql = `INSERT INTO ${table} (${column}) VALUES (?);`;
const result = await db.runAsync(insertSql, [valor]);
mensagem && Alert.alert("Sucesso", successMessage)
return result.lastInsertRowId;
} catch (error) {
//console.error(`❌ Erro ao inserir em ${table}:`, error);
mensagem && Alert.alert("Erro", `Erro ao cadastrar em ${table}.\n${error}`)
return null;
}
};
/**
* @description Atualiza um registro genérico em qualquer tabela
* @param {string} table - Nome da tabela (ex: 'cooperativa')
* @param {string} column - Nome da coluna (ex: 'nome_cooperativa')
* @param {string|number} value - Novo valor
* @param {number} id - ID do registro que será atualizado
* @param {string} successMessage - Texto de sucesso no Alert
* @returns {Promise<boolean>} true se sucesso, false caso erro
*/
export const atualizarGenerico = async (table, column, value, id, successMessage, mensagem=true) => {
const valor = value?.toString().trim();
if (!valor) {
mensagem && Alert.alert("Erro", `O campo ${column} é obrigatório.`)
return false;
}
const db = await openDatabase();
try {
// Verifica se já existe outro com o mesmo nome
const checkSql = `SELECT 1 FROM ${table} WHERE ${column} = ? AND id_${table} != ? LIMIT 1;`;
const existente = await db.getFirstAsync(checkSql, [valor, id]);
if (existente) {
mensagem && Alert.alert("Aviso", `Já existe um registro com o valor "${valor}" em ${table}.`)
return false;
}
// Atualização
const updateSql = `UPDATE ${table} SET ${column} = ? WHERE id_${table} = ?;`;
await db.runAsync(updateSql, [valor, id]);
mensagem && Alert.alert("Sucesso", successMessage)
return true;
} catch (error) {
//console.error(`❌ Erro ao atualizar ${table}:`, error);
mensagem && Alert.alert("Erro", `Erro ao atualizar em ${table}.\n${error}`)
return false;
}
};
/**
* @description Busca todos os produtores com todos os seus campos,
* e adiciona também o nome da cooperativa (ou null).
*
* @returns {Promise<Array<Object>>} Cada objeto terá todas as colunas de `produtor`
* e ainda uma propriedade `cooperativa`.
*/
export async function buscarProdutoresCooperativa() {
const db = await openDatabase();
const sql = `
SELECT
p.*,
c.nome_cooperativa AS cooperativa
FROM produtor p
LEFT JOIN cooperativa_produtor cp
ON cp.id_produtor = p.id_produtor
LEFT JOIN cooperativa c
ON c.id_cooperativa = cp.id_cooperativa;
`;
try {
const rows = await db.getAllAsync(sql);
return rows;
} catch (error) {
//console.error('❌ Erro ao buscar produtores com cooperativa:', error);
Alert.alert('Erro',`Erro ao buscar produtores com cooperativa.\n${error}`)
throw error;
}
}
/**
* @description Busca um produtor pelo ID com todos os seus campos,
* e adiciona também o nome da cooperativa (ou null) e o id da cooperativa.
*
* @param {number} idProdutor - ID do produtor a ser buscado
* @returns {Promise<Object|null>} Objeto com os dados do produtor, `cooperativa` e `id_cooperativa`, ou `null`
*/
export async function buscarProdutorPorId(idProdutor) {
const db = await openDatabase();
const sql = `
SELECT
p.*,
cp.id_cooperativa,
c.nome_cooperativa AS cooperativa
FROM produtor p
LEFT JOIN cooperativa_produtor cp
ON cp.id_produtor = p.id_produtor
LEFT JOIN cooperativa c
ON c.id_cooperativa = cp.id_cooperativa
WHERE p.id_produtor = ?;
`;
try {
const row = await db.getAllAsync(sql, [idProdutor]);
return row || null;
} catch (error) {
//console.error('❌ Erro ao buscar produtor por ID com cooperativa:', error);
Alert.alert('Erro',`Erro ao buscar produtor por ID com cooperativa.\n${error}`)
throw error;
}
}
/**
* @description Busca todas as plantações, substituindo
* - id_produtor → nome_produtor
* - id_variedade → nome_variedade
* - id_comunidade → nome_comunidade
* - id_municipio → nome_municipio
* E agrupando todas as faces de exposição numa coluna `faces_exposicao`
* e os meses de colheita como array.
*
* @returns {Promise<Array<Object>>}
* Cada objeto terá:
* id_plantacao,
* nome_plantacao,
* produtor,
* variedade,
* comunidade,
* municipio,
* latitude,
* longitude,
* altitude_media,
* nome_talhao,
* faces_exposicao, // ex: "Norte, Sul, Leste"
* meses_colheita // ex: ["Janeiro", "Março"]
*/
export async function buscarPlantacoesDetalhadas() {
const db = await openDatabase();
const sql = `
SELECT
p.id_plantacao,
p.nome_plantacao,
pr.nome_produtor AS produtor,
v.nome_variedade AS variedade,
co.nome_comunidade AS comunidade,
mu.nome_municipio AS municipio,
p.latitude,
p.longitude,
p.altitude_media,
p.nome_talhao,
p.meses_colheita,
GROUP_CONCAT(fe.nome_face_exposicao, ', ') AS faces_exposicao
FROM plantacao p
LEFT JOIN produtor pr
ON pr.id_produtor = p.id_produtor
LEFT JOIN variedade v
ON v.id_variedade = p.id_variedade
LEFT JOIN comunidade co
ON co.id_comunidade = p.id_comunidade
LEFT JOIN municipio mu
ON mu.id_municipio = p.id_municipio
LEFT JOIN face_exposicao_plantacao fep
ON fep.id_plantacao = p.id_plantacao
LEFT JOIN face_exposicao fe
ON fe.id_face_exposicao = fep.id_face_exposicao
GROUP BY p.id_plantacao
ORDER BY p.id_plantacao;
`;
try {
const rows = await db.getAllAsync(sql);
// converte meses_colheita de JSON para array
return rows.map(row => ({
...row,
meses_colheita: row.meses_colheita ? JSON.parse(row.meses_colheita) : []
}));
} catch (error) {
//console.error('❌ Erro ao buscar plantações detalhadas:', error);
Alert.alert('Erro',`Erro ao buscar plantações detalhadas.\n${error}`)
throw error;
}
}
/**
* @description Busca uma plantação pelo ID, incluindo todas as faces de exposição associadas.
*
* @param {number} idPlantacao - ID da plantação a ser buscada
* @returns {Promise<Object|null>} Objeto com dados da plantação e um array `faces` com os IDs das exposições, ou null se não encontrada
*/
export async function buscarPlantacaoPorId(idPlantacao) {
const db = await openDatabase();
const sqlPlantacao = `
SELECT *
FROM plantacao
WHERE id_plantacao = ?;
`;
const sqlFaces = `
SELECT id_face_exposicao
FROM face_exposicao_plantacao
WHERE id_plantacao = ?;
`;
try {
// Busca a plantação (como array)
const rows = await db.getAllAsync(sqlPlantacao, [idPlantacao]);
const plantacao = rows[0]; // ✅ Pega o primeiro registro
if (!plantacao) return null;
// Busca as faces associadas
const faces = await db.getAllAsync(sqlFaces, [idPlantacao]);
plantacao.faces = faces.map(f => f.id_face_exposicao);
// Trata meses_colheita
try {
plantacao.meses_colheita = plantacao.meses_colheita
? JSON.parse(plantacao.meses_colheita)
: [];
} catch {
plantacao.meses_colheita = [];
}
return plantacao;
} catch (error) {
//console.error('❌ Erro ao buscar plantação por ID:', error);
Alert.alert('Erro', `Não foi possível carregar os dados da plantação.\n${error}`);
return null;
}
}
/**
* @description Insere uma plantação e associa múltiplas faces de exposição a ela.
*
* @param {Object} plantacao
* @param {number} plantacao.id_produtor
* @param {number} plantacao.id_variedade
* @param {number} plantacao.id_comunidade
* @param {number} plantacao.id_municipio
* @param {string} plantacao.nome_plantacao
* @param {string} [plantacao.latitude]
* @param {string} [plantacao.longitude]
* @param {string} [plantacao.altitude_media]
* @param {string} [plantacao.nome_talhao]
* @param {number[]} plantacao.faces Array de IDs de face_exposicao
* @returns {Promise<number|null>} id da plantação ou null em caso de erro
*/
export const inserirPlantacao = async ({
id_produtor,
id_variedade,
id_comunidade,
id_municipio,
nome_plantacao,
latitude = null,
longitude = null,
altitude_media = null,
nome_talhao = null,
faces = [],
meses_colheita=null,
mensagem = true
}) => {
const db = await openDatabase();
// validação mínima
if (!id_produtor || !id_variedade || !id_comunidade || !id_municipio || !nome_plantacao) {
mensagem && Alert.alert('Erro', 'Campos obrigatórios não informados.')
return null;
}
try {
// inicia transação
await db.execAsync('BEGIN TRANSACTION;');
// 1) insere na plantacao
const res = await db.runAsync(
`INSERT INTO plantacao (
id_produtor, id_variedade, id_comunidade, id_municipio,
nome_plantacao, latitude, longitude, altitude_media, nome_talhao, meses_colheita
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`,
[
id_produtor,
id_variedade,
id_comunidade,
id_municipio,
nome_plantacao,
latitude,
longitude,
altitude_media,
nome_talhao,
JSON.stringify(meses_colheita) // 👈 Salva como JSON string
]
);
const id_plantacao = res.lastInsertRowId;
// 2) associa cada face de exposição
for (const id_face of faces) {
await db.runAsync(
`INSERT INTO face_exposicao_plantacao (id_face_exposicao, id_plantacao)
VALUES (?, ?);`,
[id_face, id_plantacao]
);
}
// 3) commita
await db.execAsync('COMMIT;');
mensagem && Alert.alert('Sucesso', 'Plantação cadastrada com sucesso!')
return id_plantacao;
} catch (error) {
// no erro, desfaz a transação
try { await db.execAsync('ROLLBACK;'); } catch (_) {/* ignore */}
//console.error('❌ Erro ao cadastrar plantação:', error);
mensagem && Alert.alert('Erro', `Não foi possível cadastrar a plantação.\n${error}`)
return null;
}
};
/**
* @description Atualiza uma plantação existente e suas associações de face de exposição.
*
* @param {Object} plantacao
* @param {number} plantacao.id_plantacao
* @param {number} plantacao.id_produtor
* @param {number} plantacao.id_variedade
* @param {number} plantacao.id_comunidade
* @param {number} plantacao.id_municipio
* @param {string} plantacao.nome_plantacao
* @param {string} [plantacao.latitude]
* @param {string} [plantacao.longitude]
* @param {string} [plantacao.altitude_media]
* @param {string} [plantacao.nome_talhao]
* @param {number[]} plantacao.faces Array de IDs de face_exposicao
* @param {string[]} [plantacao.meses_colheita]
* @returns {Promise<boolean>} true se sucesso, false se erro
*/
export const atualizarPlantacao = async ({
id_plantacao,
id_produtor,
id_variedade,
id_comunidade,
id_municipio,
nome_plantacao,
latitude = null,
longitude = null,
altitude_media = null,
nome_talhao = null,
faces = [],
meses_colheita = null,
mensagem = true
}) => {
const db = await openDatabase();
if (
!id_plantacao || !id_produtor || !id_variedade || !id_comunidade ||
!id_municipio || !nome_plantacao
) {
mensagem && Alert.alert('Erro', 'Campos obrigatórios não informados.')
return false;
}
try {
await db.execAsync('BEGIN TRANSACTION;');
// Atualiza a plantação
await db.runAsync(
`UPDATE plantacao SET
id_produtor = ?,
id_variedade = ?,
id_comunidade = ?,
id_municipio = ?,
nome_plantacao = ?,
latitude = ?,
longitude = ?,
altitude_media = ?,
nome_talhao = ?,
meses_colheita = ?
WHERE id_plantacao = ?;`,
[
id_produtor,
id_variedade,
id_comunidade,
id_municipio,
nome_plantacao,
latitude,
longitude,
altitude_media,
nome_talhao,
JSON.stringify(meses_colheita),
id_plantacao
]
);
// Remove faces antigas
await db.runAsync(
`DELETE FROM face_exposicao_plantacao WHERE id_plantacao = ?;`,
[id_plantacao]
);
// Insere novas faces
for (const id_face of faces) {
await db.runAsync(
`INSERT INTO face_exposicao_plantacao (id_face_exposicao, id_plantacao)
VALUES (?, ?);`,
[id_face, id_plantacao]
);
}
await db.execAsync('COMMIT;');
mensagem && Alert.alert('Sucesso', 'Plantação atualizada com sucesso!')
return true;
} catch (error) {
try { await db.execAsync('ROLLBACK;'); } catch (_) {}
//console.error('❌ Erro ao atualizar plantação:', error);
mensagem && Alert.alert('Erro', `Não foi possível atualizar a plantação.\n${error}`)
return false;
}
};
/**
*
* @param {String} tabela
* @param {String} termoBusca
* @returns {Promise<Object|null>} Retorna a busca feita no filtro
*/
export const buscarRegistrosComFiltro = async (tabela, termoBusca) => {
const db = await openDatabase();
try {
// 1. Tenta identificar colunas de texto via PRAGMA
const colunasInfo = await db.getAllAsync(`PRAGMA table_info(${tabela});`);
let colunasTexto = colunasInfo
.filter(col =>
col.type?.toUpperCase().includes('CHAR') || col.type?.toUpperCase().includes('TEXT')
)
.map(col => col.name);
// 2. Fallback: se nada for identificado, tenta a coluna que começa com "nome_"
if (colunasTexto.length === 0) {
colunasTexto = colunasInfo
.filter(col => col.name?.toLowerCase().startsWith('nome_'))
.map(col => col.name);
}
// 3. Se ainda assim não achou nada, retorna []
if (colunasTexto.length === 0) return [];
// 4. Monta a cláusula WHERE
const whereClause = colunasTexto.map(col => `${col} LIKE ?`).join(' OR ');
const params = colunasTexto.map(() => `%${termoBusca}%`);
const sql = `SELECT * FROM ${tabela} WHERE ${whereClause};`;
const resultados = await db.getAllAsync(sql, params);
return resultados;
} catch (error) {
//console.error(`Erro ao buscar com filtro na tabela ${tabela}:`, error);
Alert.alert('Erro',`Erro ao buscar com filtro na tabela ${tabela}.\n${error}`)
return [];
}
};
///////////////////////// EXCLUIR ///////////////////////////////////////////////
/**
* @description Exclui uma plantação
* @param {String} id_plantacao
*
*/
export const excluirPlantacao = async (id_plantacao) => {
const db = await openDatabase();
try {
await db.execAsync('BEGIN TRANSACTION;');
// Remove associações com faces de exposição
await db.runAsync(
`DELETE FROM face_exposicao_plantacao WHERE id_plantacao = ?;`,
[id_plantacao]
);
// Remove a plantação
await db.runAsync(
`DELETE FROM plantacao WHERE id_plantacao = ?;`,
[id_plantacao]
);
await db.execAsync('COMMIT;');
Alert.alert('Sucesso', 'Plantação excluída com sucesso!');
return true;
} catch (error) {
try { await db.execAsync('ROLLBACK;'); } catch (_) {}
//console.error('❌ Erro ao excluir plantação:', error);
Alert.alert('Erro', `Erro ao excluir a plantação.\n${error}`);
return false;
}
};
/**
* @description Exclui um produtor
* @param {*} id_produtor
*
*/
export const excluirProdutor = async (id_produtor) => {
const db = await openDatabase();
try {
await db.execAsync('BEGIN TRANSACTION;');
// Verifica se há plantações associadas
const plantaExistente = await db.getFirstAsync(
`SELECT 1 FROM plantacao WHERE id_produtor = ? LIMIT 1;`,
[id_produtor]
);
if (plantaExistente) {
Alert.alert('Aviso', 'Este produtor está vinculado a plantações e não pode ser excluído.');
await db.execAsync('ROLLBACK;');
return false;
}
// Remove associação com cooperativa (se houver)
await db.runAsync(
`DELETE FROM cooperativa_produtor WHERE id_produtor = ?;`,
[id_produtor]
);
// Remove o produtor
await db.runAsync(
`DELETE FROM produtor WHERE id_produtor = ?;`,
[id_produtor]
);
await db.execAsync('COMMIT;');
Alert.alert('Sucesso', 'Produtor excluído com sucesso!');
return true;
} catch (error) {
try { await db.execAsync('ROLLBACK;'); } catch (_) {}
//console.error('❌ Erro ao excluir produtor:', error);
Alert.alert('Erro', `Erro ao excluir o produtor.\n${error}`);
return false;
}
};
/**
* @description Excluir dados genéricos
* @param {*} tabela
* @param {*} id
*
*/
export const excluirGenerico = async (tabela, id) => {
const db = await openDatabase();
const idColumnMap = {
cooperativa: 'id_cooperativa',
municipio: 'id_municipio',
comunidade: 'id_comunidade',
face_exposicao: 'id_face_exposicao',
variedade: 'id_variedade',
};
const dependenciasMap = {
cooperativa: { tabela: 'cooperativa_produtor', coluna: 'id_cooperativa' },
municipio: { tabela: 'plantacao', coluna: 'id_municipio' },
comunidade: { tabela: 'plantacao', coluna: 'id_comunidade' },
face_exposicao: { tabela: 'face_exposicao_plantacao', coluna: 'id_face_exposicao' },
variedade: { tabela: 'plantacao', coluna: 'id_variedade' },
};
const idColumn = idColumnMap[tabela];
const dependenteInfo = dependenciasMap[tabela];
if (!idColumn || !dependenteInfo) {
Alert.alert('Erro', `Configuração de exclusão inválida para tabela '${tabela}'.`);
return false;
}
try {
await db.execAsync('BEGIN TRANSACTION;');
const dependente = await db.getFirstAsync(
`SELECT 1 FROM ${dependenteInfo.tabela} WHERE ${dependenteInfo.coluna} = ? LIMIT 1;`,
[id]
);
if (dependente) {
Alert.alert('Aviso', 'Este registro está vinculado a outro e não pode ser excluído.');
await db.execAsync('ROLLBACK;');
return false;
}
await db.runAsync(
`DELETE FROM ${tabela} WHERE ${idColumn} = ?;`,
[id]
);
await db.execAsync('COMMIT;');
Alert.alert('Sucesso', `Registro excluído de ${tabela}.`);
return true;
} catch (error) {
try { await db.execAsync('ROLLBACK;'); } catch (_) {}
//console.error(`❌ Erro ao excluir em ${tabela}:`, error);
Alert.alert('Erro', `Erro ao excluir o registro de ${tabela}.\n${error}`);
return false;
}
};
/**
* @description Retorna todos os dados em uma única tabela unificada, com:
* - nome_produtor, cpf, codigo_produtor, cooperativa
* - nome_plantacao, variedade, comunidade, municipio
* - latitude, longitude, altitude_media, nome_talhao
* - faces_exposicao (string), meses_colheita (array)
*
* @returns {Promise<Array<Object>>}
*/
export async function buscarTudoUnificado() {
const db = await openDatabase();
const sql = `
SELECT
p.nome_produtor AS "Nome do Produtor",
p.cpf_produtor AS "CPF do Produtor",
p.codigo_produtor AS "Código do Produtor",
c.nome_cooperativa AS "Cooperativa",
pl.nome_plantacao AS "Nome da Plantação",
v.nome_variedade AS "Variedade",
com.nome_comunidade AS "Comunidade",
m.nome_municipio AS "Município",
pl.latitude AS "Latitude",
pl.longitude AS "Longitude",
pl.altitude_media AS "Altitude Média",
pl.nome_talhao AS "Nome do Talhão",
pl.meses_colheita AS "Meses de Colheita",
GROUP_CONCAT(fe.nome_face_exposicao, ', ') AS "Faces de Exposição"
FROM produtor p
LEFT JOIN cooperativa_produtor cp
ON cp.id_produtor = p.id_produtor
LEFT JOIN cooperativa c
ON c.id_cooperativa = cp.id_cooperativa
LEFT JOIN plantacao pl
ON pl.id_produtor = p.id_produtor
LEFT JOIN variedade v
ON v.id_variedade = pl.id_variedade
LEFT JOIN comunidade com
ON com.id_comunidade = pl.id_comunidade
LEFT JOIN municipio m
ON m.id_municipio = pl.id_municipio
LEFT JOIN face_exposicao_plantacao fep
ON fep.id_plantacao = pl.id_plantacao
LEFT JOIN face_exposicao fe
ON fe.id_face_exposicao = fep.id_face_exposicao
GROUP BY pl.id_plantacao
ORDER BY p.nome_produtor, pl.nome_plantacao;
`;
try {
const rows = await db.getAllAsync(sql);
return rows.map(row => ({
...row,
"Meses de Colheita": row["Meses de Colheita"]
? JSON.parse(row["Meses de Colheita"])
: [],
}));
} catch (error) {
//console.error('❌ Erro ao buscar dados unificados:', error);
Alert.alert('Erro', `Não foi possível carregar os dados completos.\n${error}`);
return [];
}
}