refactor(backend): migrate all queries to native PostgreSQL, remove MySQL shim
- Remove translateQuery function, pool.query override, and pool.getConnection mock entirely
- Replace all ? placeholders with $N positional parameters throughout
- Replace INSERT INTO t SET ? with buildInsert() helper across pacientes, dentistas, especialidades, planos, procedimentos, financeiro, leads, gto_items
- Replace UPDATE t SET ? WHERE id = ? with buildUpdate() helper across the same tables
- Replace all pool.getConnection()/beginTransaction/commit/rollback blocks with withTransaction()
- Convert ON DUPLICATE KEY UPDATE to ON CONFLICT DO UPDATE SET in dentistas/register (usuarios, vinculos, dentistas tables)
- Fix dentistas/register to use withTransaction instead of SET FOREIGN_KEY_CHECKS
- Fix all [rows] array destructuring to { rows } object destructuring (PG native result shape)
- Fix guias dynamic WHERE builder to use incrementing $N parameter indices
- Fix double-quoted string literal bug in procedimentos categoria query ("Particular" → 'Particular')
- Quote camelCase column names in gto_items/gto_builders queries ("builderId", "valorTotal", etc.)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+136
-227
@@ -22,94 +22,6 @@ const pool = new Pool({
|
|||||||
pool.on('connect', () => console.log('[PG] Connected to PostgreSQL'));
|
pool.on('connect', () => console.log('[PG] Connected to PostgreSQL'));
|
||||||
pool.on('error', (err) => console.error('[PG] Pool error:', err.message));
|
pool.on('error', (err) => console.error('[PG] Pool error:', err.message));
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// PostgreSQL Translator & Compatibility Wrapper
|
|
||||||
// =============================================================================
|
|
||||||
function translateQuery(query, params) {
|
|
||||||
if (typeof query !== 'string') return { sql: query, pgParams: params };
|
|
||||||
let sql = query;
|
|
||||||
let pgParams = params ? (Array.isArray(params) ? [...params] : [params]) : [];
|
|
||||||
|
|
||||||
sql = sql.replace(/`([^`]+)`/g, '"$1"');
|
|
||||||
|
|
||||||
if (sql.match(/INSERT\s+INTO\s+([^\s]+)\s+SET\s+\?/i)) {
|
|
||||||
const table = sql.match(/INSERT\s+INTO\s+([^\s]+)\s+SET\s+\?/i)[1].replace(/"/g, '');
|
|
||||||
const dataObj = pgParams[0];
|
|
||||||
if (dataObj && typeof dataObj === 'object') {
|
|
||||||
const keys = Object.keys(dataObj);
|
|
||||||
const cols = keys.map(k => `"${k}"`).join(', ');
|
|
||||||
const valuesPlaceholder = keys.map((_, idx) => `$${idx + 1}`).join(', ');
|
|
||||||
sql = `INSERT INTO "${table}" (${cols}) VALUES (${valuesPlaceholder})`;
|
|
||||||
pgParams = keys.map(k => dataObj[k]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sql.match(/UPDATE\s+([^\s]+)\s+SET\s+\?\s+WHERE\s+(.+)/i)) {
|
|
||||||
const match = sql.match(/UPDATE\s+([^\s]+)\s+SET\s+\?\s+WHERE\s+(.+)/i);
|
|
||||||
const table = match[1].replace(/"/g, '');
|
|
||||||
const whereClause = match[2];
|
|
||||||
const dataObj = pgParams[0];
|
|
||||||
const extraParams = pgParams.slice(1);
|
|
||||||
if (dataObj && typeof dataObj === 'object') {
|
|
||||||
const keys = Object.keys(dataObj);
|
|
||||||
let paramIdx = 1;
|
|
||||||
const setClause = keys.map(k => `"${k}" = $${paramIdx++}`).join(', ');
|
|
||||||
let postgresWhere = whereClause;
|
|
||||||
let tempIdx = paramIdx;
|
|
||||||
postgresWhere = postgresWhere.replace(/\?/g, () => `$${tempIdx++}`);
|
|
||||||
sql = `UPDATE "${table}" SET ${setClause} WHERE ${postgresWhere}`;
|
|
||||||
pgParams = [...keys.map(k => dataObj[k]), ...extraParams];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let count = 1;
|
|
||||||
sql = sql.replace(/\?/g, () => `$${count++}`);
|
|
||||||
|
|
||||||
if (sql.match(/ON\s+DUPLICATE\s+KEY\s+UPDATE/i)) {
|
|
||||||
if (sql.toLowerCase().includes('settings')) sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE\s+data\s*=\s*VALUES\(data\)|ON\s+DUPLICATE\s+KEY\s+UPDATE\s+data\s*=\s*\$4|ON\s+DUPLICATE\s+KEY\s+UPDATE\s+data\s*=\s*EXCLUDED\.data/i, 'ON CONFLICT (id) DO UPDATE SET data = EXCLUDED.data');
|
|
||||||
else if (sql.toLowerCase().includes('google_tokens')) sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE[\s\S]+/i, 'ON CONFLICT (owner_id) DO UPDATE SET refresh_token = EXCLUDED.refresh_token, access_token = EXCLUDED.access_token, expiry_date = EXCLUDED.expiry_date');
|
|
||||||
else if (sql.toLowerCase().includes('usuarios')) sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE[\s\S]+/i, 'ON CONFLICT (id) DO UPDATE SET nome = EXCLUDED.nome, cro = EXCLUDED.cro, cro_uf = EXCLUDED.cro_uf, celular = EXCLUDED.celular, especialidade = EXCLUDED.especialidade');
|
|
||||||
else if (sql.toLowerCase().includes('vinculos')) sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE[\s\S]+/i, 'ON CONFLICT (usuario_id, clinica_id) DO UPDATE SET role = EXCLUDED.role');
|
|
||||||
else if (sql.toLowerCase().includes('dentistas_horarios')) sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE[\s\S]+/i, 'ON CONFLICT (id) DO UPDATE SET dia_semana = EXCLUDED.dia_semana, hora_inicio = EXCLUDED.hora_inicio, hora_fim = EXCLUDED.hora_fim, ativo = EXCLUDED.ativo');
|
|
||||||
else if (sql.toLowerCase().includes('dentistas')) sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE[\s\S]+/i, 'ON CONFLICT (id) DO UPDATE SET nome = EXCLUDED.nome, especialidade = EXCLUDED.especialidade');
|
|
||||||
else if (sql.toLowerCase().includes('procedimentos_valores_planos')) sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE[\s\S]+/i, 'ON CONFLICT (id) DO UPDATE SET valor = EXCLUDED.valor');
|
|
||||||
else if (sql.toLowerCase().includes('procedimentos')) sql = sql.replace(/ON\s+DUPLICATE\s+KEY\s+UPDATE[\s\S]+/i, 'ON CONFLICT (id) DO UPDATE SET nome = EXCLUDED.nome, "especialidadeId" = EXCLUDED."especialidadeId", "especialidadeNome" = EXCLUDED."especialidadeNome", "valorParticular" = EXCLUDED."valorParticular", "codigoInterno" = EXCLUDED."codigoInterno", categoria = EXCLUDED.categoria, tipo_regiao = EXCLUDED.tipo_regiao, exige_face = EXCLUDED.exige_face, codigo = EXCLUDED.codigo');
|
|
||||||
}
|
|
||||||
|
|
||||||
sql = sql.replace(/DATE_FORMAT\(([^,]+),\s*['"]%Y-%m['"]\)/gi, "TO_CHAR($1, 'YYYY-MM')");
|
|
||||||
sql = sql.replace(/DATE_SUB\(CURDATE\(\),\s*INTERVAL\s+(\d+)\s+MONTH\)/gi, "CURRENT_DATE - INTERVAL '$1 MONTH'");
|
|
||||||
if (sql.match(/SET\s+FOREIGN_KEY_CHECKS\s*=\s*\d+/i)) sql = "SELECT 1";
|
|
||||||
|
|
||||||
return { sql, pgParams };
|
|
||||||
}
|
|
||||||
|
|
||||||
const originalPoolQuery = pool.query.bind(pool);
|
|
||||||
pool.query = async function (queryText, params) {
|
|
||||||
const { sql, pgParams } = translateQuery(queryText, params);
|
|
||||||
const result = await originalPoolQuery(sql, pgParams);
|
|
||||||
const resArray = [result.rows, result.fields];
|
|
||||||
resArray.rows = result.rows;
|
|
||||||
resArray.rowCount = result.rowCount;
|
|
||||||
return resArray;
|
|
||||||
};
|
|
||||||
|
|
||||||
pool.getConnection = async function () {
|
|
||||||
const client = await pool.connect();
|
|
||||||
const originalClientQuery = client.query.bind(client);
|
|
||||||
client.query = async function (queryText, params) {
|
|
||||||
const { sql, pgParams } = translateQuery(queryText, params);
|
|
||||||
const result = await originalClientQuery(sql, pgParams);
|
|
||||||
const resArray = [result.rows, result.fields];
|
|
||||||
resArray.rows = result.rows;
|
|
||||||
resArray.rowCount = result.rowCount;
|
|
||||||
return resArray;
|
|
||||||
};
|
|
||||||
client.beginTransaction = async () => await originalClientQuery('BEGIN');
|
|
||||||
client.commit = async () => await originalClientQuery('COMMIT');
|
|
||||||
client.rollback = async () => await originalClientQuery('ROLLBACK');
|
|
||||||
return client;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper: transaction wrapper
|
// Helper: transaction wrapper
|
||||||
async function withTransaction(fn) {
|
async function withTransaction(fn) {
|
||||||
const client = await pool.connect();
|
const client = await pool.connect();
|
||||||
@@ -312,7 +224,7 @@ app.get('/api/oauth2callback', async (req, res) => {
|
|||||||
|
|
||||||
app.get('/api/auth/google/status', async (req, res) => {
|
app.get('/api/auth/google/status', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const [rows] = await pool.query('SELECT owner_id, updated_at FROM google_tokens');
|
const { rows } = await pool.query('SELECT owner_id, updated_at FROM google_tokens');
|
||||||
res.json(rows);
|
res.json(rows);
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
@@ -358,33 +270,32 @@ app.post('/api/dentistas/magic-link', async (req, res) => {
|
|||||||
app.post('/api/dentistas/register', async (req, res) => {
|
app.post('/api/dentistas/register', async (req, res) => {
|
||||||
const { id, nome, email, senha, cro, cro_uf, celular, especialidade, clinicaId } = req.body;
|
const { id, nome, email, senha, cro, cro_uf, celular, especialidade, clinicaId } = req.body;
|
||||||
try {
|
try {
|
||||||
await pool.query('SET FOREIGN_KEY_CHECKS = 0');
|
await withTransaction(async (client) => {
|
||||||
// 1. Create User
|
// 1. Create or update User
|
||||||
await pool.query(`
|
await client.query(`
|
||||||
INSERT INTO usuarios (id, nome, email, senha, cro, cro_uf, celular, especialidade)
|
INSERT INTO usuarios (id, nome, email, senha, cro, cro_uf, celular, especialidade)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||||
ON DUPLICATE KEY UPDATE
|
ON CONFLICT (id) DO UPDATE SET
|
||||||
nome = VALUES(nome), cro = VALUES(cro), cro_uf = VALUES(cro_uf),
|
nome = EXCLUDED.nome, cro = EXCLUDED.cro, cro_uf = EXCLUDED.cro_uf,
|
||||||
celular = VALUES(celular), especialidade = VALUES(especialidade)
|
celular = EXCLUDED.celular, especialidade = EXCLUDED.especialidade
|
||||||
`, [id, nome, email, senha, cro, cro_uf, celular, especialidade]);
|
`, [id, nome, email, senha, cro, cro_uf, celular, especialidade]);
|
||||||
|
|
||||||
// 2. Create Link to clinic
|
// 2. Create or update link to clinic
|
||||||
const vinculoId = `v_${id}_${clinicaId}`;
|
const vinculoId = `v_${id}_${clinicaId}`;
|
||||||
await pool.query(`
|
await client.query(`
|
||||||
INSERT INTO vinculos (id, usuario_id, clinica_id, role)
|
INSERT INTO vinculos (id, usuario_id, clinica_id, role)
|
||||||
VALUES (?, ?, ?, 'dentista')
|
VALUES ($1, $2, $3, 'dentista')
|
||||||
ON DUPLICATE KEY UPDATE role = 'dentista'
|
ON CONFLICT (usuario_id, clinica_id) DO UPDATE SET role = 'dentista'
|
||||||
`, [vinculoId, id, clinicaId]);
|
`, [vinculoId, id, clinicaId]);
|
||||||
|
|
||||||
// 3. Create Dentist record for agenda
|
// 3. Create or update Dentist record for agenda
|
||||||
await pool.query(`
|
await client.query(`
|
||||||
INSERT INTO dentistas (id, clinica_id, nome, email, telefone, especialidade, corAgenda)
|
INSERT INTO dentistas (id, clinica_id, nome, email, telefone, especialidade, "corAgenda")
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
ON DUPLICATE KEY UPDATE
|
ON CONFLICT (id) DO UPDATE SET
|
||||||
nome = VALUES(nome), especialidade = VALUES(especialidade)
|
nome = EXCLUDED.nome, especialidade = EXCLUDED.especialidade
|
||||||
`, [id, clinicaId, nome, email, celular, especialidade, '#2563eb']);
|
`, [id, clinicaId, nome, email, celular, especialidade, '#2563eb']);
|
||||||
|
});
|
||||||
await pool.query('SET FOREIGN_KEY_CHECKS = 1');
|
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
@@ -392,7 +303,7 @@ app.post('/api/dentistas/register', async (req, res) => {
|
|||||||
app.get('/api/google/events', async (req, res) => {
|
app.get('/api/google/events', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// 1. Check for connected accounts in google_tokens
|
// 1. Check for connected accounts in google_tokens
|
||||||
const [tokenRows] = await pool.query('SELECT * FROM google_tokens');
|
const { rows: tokenRows } = await pool.query('SELECT * FROM google_tokens');
|
||||||
const allEvents = [];
|
const allEvents = [];
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const timeMin = now.toISOString();
|
const timeMin = now.toISOString();
|
||||||
@@ -418,7 +329,7 @@ app.get('/api/google/events', async (req, res) => {
|
|||||||
if (response.data.items) {
|
if (response.data.items) {
|
||||||
// Try to resolve owner name
|
// Try to resolve owner name
|
||||||
let ownerName = tokenData.owner_id;
|
let ownerName = tokenData.owner_id;
|
||||||
const [dent] = await pool.query('SELECT nome FROM dentistas WHERE id = ?', [tokenData.owner_id]);
|
const { rows: dent } = await pool.query('SELECT nome FROM dentistas WHERE id = $1', [tokenData.owner_id]);
|
||||||
if (dent[0]) ownerName = dent[0].nome;
|
if (dent[0]) ownerName = dent[0].nome;
|
||||||
|
|
||||||
const mapped = response.data.items.map(item => ({
|
const mapped = response.data.items.map(item => ({
|
||||||
@@ -439,9 +350,9 @@ app.get('/api/google/events', async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. Legacy Support: Check Settings for Public Calendars (API KEY)
|
// 2. Legacy Support: Check Settings for Public Calendars (API KEY)
|
||||||
const [rows] = await pool.query("SELECT data FROM settings WHERE id = 'main'");
|
const { rows: settingsRows } = await pool.query("SELECT data FROM settings WHERE id = 'main'");
|
||||||
if (rows[0]) {
|
if (settingsRows[0]) {
|
||||||
const settings = JSON.parse(rows[0].data);
|
const settings = JSON.parse(settingsRows[0].data);
|
||||||
const apiKey = settings.googleApiKey;
|
const apiKey = settings.googleApiKey;
|
||||||
if (apiKey) {
|
if (apiKey) {
|
||||||
const legacyIds = [];
|
const legacyIds = [];
|
||||||
@@ -450,7 +361,7 @@ app.get('/api/google/events', async (req, res) => {
|
|||||||
if (settings.googleCalendarIds) {
|
if (settings.googleCalendarIds) {
|
||||||
for (const [dentistId, calId] of Object.entries(settings.googleCalendarIds)) {
|
for (const [dentistId, calId] of Object.entries(settings.googleCalendarIds)) {
|
||||||
if (calId && !tokenRows.find(t => t.owner_id === dentistId)) {
|
if (calId && !tokenRows.find(t => t.owner_id === dentistId)) {
|
||||||
const [dName] = await pool.query('SELECT nome FROM dentistas WHERE id = ?', [dentistId]);
|
const { rows: dName } = await pool.query('SELECT nome FROM dentistas WHERE id = $1', [dentistId]);
|
||||||
legacyIds.push({ id: calId, owner: dName[0]?.nome || 'DENTISTA' });
|
legacyIds.push({ id: calId, owner: dName[0]?.nome || 'DENTISTA' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -576,7 +487,7 @@ app.put('/api/notificacoes/:id/read', async (req, res) => {
|
|||||||
// --- PACIENTES ---
|
// --- PACIENTES ---
|
||||||
app.get('/api/pacientes', async (req, res) => {
|
app.get('/api/pacientes', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const [rows] = await pool.query('SELECT * FROM pacientes');
|
const { rows } = await pool.query('SELECT * FROM pacientes');
|
||||||
res.json(rows);
|
res.json(rows);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.status(500).json({ error: err.message });
|
res.status(500).json({ error: err.message });
|
||||||
@@ -586,7 +497,8 @@ app.get('/api/pacientes', async (req, res) => {
|
|||||||
app.post('/api/pacientes', async (req, res) => {
|
app.post('/api/pacientes', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const p = req.body;
|
const p = req.body;
|
||||||
await pool.query('INSERT INTO pacientes SET ?', p);
|
const ins = buildInsert('pacientes', p);
|
||||||
|
await pool.query(ins.text, ins.values);
|
||||||
res.json({ success: true, data: p });
|
res.json({ success: true, data: p });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.status(500).json({ error: err.message });
|
res.status(500).json({ error: err.message });
|
||||||
@@ -595,7 +507,8 @@ app.post('/api/pacientes', async (req, res) => {
|
|||||||
|
|
||||||
app.put('/api/pacientes/:id', async (req, res) => {
|
app.put('/api/pacientes/:id', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await pool.query('UPDATE pacientes SET ? WHERE id = ?', [req.body, req.params.id]);
|
const upd = buildUpdate('pacientes', req.body, 'id', req.params.id);
|
||||||
|
await pool.query(upd.text, upd.values);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.status(500).json({ error: err.message });
|
res.status(500).json({ error: err.message });
|
||||||
@@ -609,11 +522,11 @@ app.get('/api/dentistas', async (req, res) => {
|
|||||||
let query = 'SELECT * FROM dentistas';
|
let query = 'SELECT * FROM dentistas';
|
||||||
const params = [];
|
const params = [];
|
||||||
if (clinicaId) {
|
if (clinicaId) {
|
||||||
query += ' WHERE clinica_id = ?';
|
query += ' WHERE clinica_id = $1';
|
||||||
params.push(clinicaId);
|
params.push(clinicaId);
|
||||||
}
|
}
|
||||||
query += ' ORDER BY ordem ASC, nome ASC';
|
query += ' ORDER BY ordem ASC, nome ASC';
|
||||||
const [rows] = await pool.query(query, params);
|
const { rows } = await pool.query(query, params);
|
||||||
res.json(rows.map(d => ({ ...d, ativo: !!d.ativo })));
|
res.json(rows.map(d => ({ ...d, ativo: !!d.ativo })));
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
@@ -622,62 +535,53 @@ app.post('/api/dentistas', async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const d = req.body;
|
const d = req.body;
|
||||||
// Automatically set order to the end
|
// Automatically set order to the end
|
||||||
const [counts] = await pool.query('SELECT COUNT(*) as count FROM dentistas');
|
const { rows: counts } = await pool.query('SELECT COUNT(*) as count FROM dentistas');
|
||||||
const ordem = counts[0].count;
|
const ordem = parseInt(counts[0].count);
|
||||||
await pool.query('INSERT INTO dentistas SET ?', { ...d, ordem });
|
const ins = buildInsert('dentistas', { ...d, ordem });
|
||||||
|
await pool.query(ins.text, ins.values);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
app.put('/api/dentistas/reorder', async (req, res) => {
|
app.put('/api/dentistas/reorder', async (req, res) => {
|
||||||
const { orders } = req.body; // Array of {id, ordem}
|
const { orders } = req.body; // Array of {id, ordem}
|
||||||
const connection = await pool.getConnection();
|
|
||||||
try {
|
try {
|
||||||
await connection.beginTransaction();
|
await withTransaction(async (client) => {
|
||||||
for (const item of orders) {
|
for (const item of orders) {
|
||||||
await connection.query('UPDATE dentistas SET ordem = ? WHERE id = ?', [item.ordem, item.id]);
|
await client.query('UPDATE dentistas SET ordem = $1 WHERE id = $2', [item.ordem, item.id]);
|
||||||
}
|
}
|
||||||
await connection.commit();
|
});
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await connection.rollback();
|
|
||||||
res.status(500).json({ error: err.message });
|
res.status(500).json({ error: err.message });
|
||||||
} finally { connection.release(); }
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.put('/api/dentistas/:id', async (req, res) => {
|
app.put('/api/dentistas/:id', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await pool.query('UPDATE dentistas SET ? WHERE id = ?', [req.body, req.params.id]);
|
const upd = buildUpdate('dentistas', req.body, 'id', req.params.id);
|
||||||
|
await pool.query(upd.text, upd.values);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
app.delete('/api/dentistas/:id', async (req, res) => {
|
app.delete('/api/dentistas/:id', async (req, res) => {
|
||||||
const connection = await pool.getConnection();
|
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
await connection.beginTransaction();
|
await withTransaction(async (client) => {
|
||||||
|
|
||||||
// 1. Remove Google Tokens associated with this dentist
|
// 1. Remove Google Tokens associated with this dentist
|
||||||
await connection.query('DELETE FROM google_tokens WHERE owner_id = ?', [id]);
|
await client.query('DELETE FROM google_tokens WHERE owner_id = $1', [id]);
|
||||||
|
|
||||||
// 2. Remove clinical links (if any)
|
// 2. Remove clinical links (if any)
|
||||||
await connection.query('DELETE FROM vinculos WHERE usuario_id = ?', [id]);
|
await client.query('DELETE FROM vinculos WHERE usuario_id = $1', [id]);
|
||||||
|
|
||||||
// 3. Set to NULL in agendamentos if it exists
|
// 3. Set to NULL in agendamentos if it exists
|
||||||
await connection.query('UPDATE agendamentos SET dentistaId = NULL WHERE dentistaId = ?', [id]);
|
await client.query('UPDATE agendamentos SET dentistaid = NULL WHERE dentistaid = $1', [id]);
|
||||||
|
|
||||||
// 4. Delete from dentistas table
|
// 4. Delete from dentistas table
|
||||||
await connection.query('DELETE FROM dentistas WHERE id = ?', [id]);
|
await client.query('DELETE FROM dentistas WHERE id = $1', [id]);
|
||||||
|
});
|
||||||
await connection.commit();
|
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await connection.rollback();
|
|
||||||
console.error('Error deleting dentist:', err);
|
console.error('Error deleting dentist:', err);
|
||||||
res.status(500).json({ error: err.message });
|
res.status(500).json({ error: err.message });
|
||||||
} finally {
|
|
||||||
connection.release();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -686,14 +590,14 @@ app.get('/api/dentistas/:dentistaId/horarios', async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const { dentistaId } = req.params;
|
const { dentistaId } = req.params;
|
||||||
const { clinicaId } = req.query;
|
const { clinicaId } = req.query;
|
||||||
let query = 'SELECT * FROM dentistas_horarios WHERE dentista_id = ?';
|
let query = 'SELECT * FROM dentistas_horarios WHERE dentista_id = $1';
|
||||||
const params = [dentistaId];
|
const params = [dentistaId];
|
||||||
if (clinicaId) {
|
if (clinicaId) {
|
||||||
query += ' AND clinica_id = ?';
|
query += ' AND clinica_id = $2';
|
||||||
params.push(clinicaId);
|
params.push(clinicaId);
|
||||||
}
|
}
|
||||||
query += ' ORDER BY dia_semana ASC, hora_inicio ASC';
|
query += ' ORDER BY dia_semana ASC, hora_inicio ASC';
|
||||||
const [rows] = await pool.query(query, params);
|
const { rows } = await pool.query(query, params);
|
||||||
res.json(rows);
|
res.json(rows);
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
@@ -701,20 +605,21 @@ app.get('/api/dentistas/:dentistaId/horarios', async (req, res) => {
|
|||||||
app.post('/api/dentistas/horarios', async (req, res) => {
|
app.post('/api/dentistas/horarios', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { id, dentista_id, clinica_id, dia_semana, hora_inicio, hora_fim, ativo } = req.body;
|
const { id, dentista_id, clinica_id, dia_semana, hora_inicio, hora_fim, ativo } = req.body;
|
||||||
|
const horaId = id || `h_${Math.random().toString(36).substring(2, 9)}`;
|
||||||
await pool.query(`
|
await pool.query(`
|
||||||
INSERT INTO dentistas_horarios (id, dentista_id, clinica_id, dia_semana, hora_inicio, hora_fim, ativo)
|
INSERT INTO dentistas_horarios (id, dentista_id, clinica_id, dia_semana, hora_inicio, hora_fim, ativo)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
ON DUPLICATE KEY UPDATE
|
ON CONFLICT (id) DO UPDATE SET
|
||||||
dia_semana = VALUES(dia_semana), hora_inicio = VALUES(hora_inicio),
|
dia_semana = EXCLUDED.dia_semana, hora_inicio = EXCLUDED.hora_inicio,
|
||||||
hora_fim = VALUES(hora_fim), ativo = VALUES(ativo)
|
hora_fim = EXCLUDED.hora_fim, ativo = EXCLUDED.ativo
|
||||||
`, [id || `h_${Math.random().toString(36).substring(2, 9)}`, dentista_id, clinica_id, dia_semana, hora_inicio, hora_fim, ativo ?? 1]);
|
`, [horaId, dentista_id, clinica_id, dia_semana, hora_inicio, hora_fim, ativo ?? 1]);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
app.delete('/api/dentistas/horarios/:id', async (req, res) => {
|
app.delete('/api/dentistas/horarios/:id', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await pool.query('DELETE FROM dentistas_horarios WHERE id = ?', [req.params.id]);
|
await pool.query('DELETE FROM dentistas_horarios WHERE id = $1', [req.params.id]);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
@@ -849,7 +754,7 @@ app.put('/api/agendamentos/:id', async (req, res) => {
|
|||||||
|
|
||||||
app.delete('/api/agendamentos/:id', async (req, res) => {
|
app.delete('/api/agendamentos/:id', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await pool.query('DELETE FROM agendamentos WHERE id = ?', [req.params.id]);
|
await pool.query('DELETE FROM agendamentos WHERE id = $1', [req.params.id]);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
@@ -857,7 +762,7 @@ app.delete('/api/agendamentos/:id', async (req, res) => {
|
|||||||
// --- ESPECIALIDADES ---
|
// --- ESPECIALIDADES ---
|
||||||
app.get('/api/especialidades', async (req, res) => {
|
app.get('/api/especialidades', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const [rows] = await pool.query('SELECT * FROM especialidades ORDER BY ordem ASC, nome ASC');
|
const { rows } = await pool.query('SELECT * FROM especialidades ORDER BY ordem ASC, nome ASC');
|
||||||
res.json(rows);
|
res.json(rows);
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
@@ -865,39 +770,39 @@ app.get('/api/especialidades', async (req, res) => {
|
|||||||
app.post('/api/especialidades', async (req, res) => {
|
app.post('/api/especialidades', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// Automatically set order to the end
|
// Automatically set order to the end
|
||||||
const [counts] = await pool.query('SELECT COUNT(*) as count FROM especialidades');
|
const { rows: counts } = await pool.query('SELECT COUNT(*) as count FROM especialidades');
|
||||||
const ordem = counts[0].count;
|
const ordem = parseInt(counts[0].count);
|
||||||
await pool.query('INSERT INTO especialidades SET ?', { ...req.body, ordem });
|
const ins = buildInsert('especialidades', { ...req.body, ordem });
|
||||||
|
await pool.query(ins.text, ins.values);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
app.put('/api/especialidades/reorder', async (req, res) => {
|
app.put('/api/especialidades/reorder', async (req, res) => {
|
||||||
const { orders } = req.body; // Array of {id, ordem}
|
const { orders } = req.body; // Array of {id, ordem}
|
||||||
const connection = await pool.getConnection();
|
|
||||||
try {
|
try {
|
||||||
await connection.beginTransaction();
|
await withTransaction(async (client) => {
|
||||||
for (const item of orders) {
|
for (const item of orders) {
|
||||||
await connection.query('UPDATE especialidades SET ordem = ? WHERE id = ?', [item.ordem, item.id]);
|
await client.query('UPDATE especialidades SET ordem = $1 WHERE id = $2', [item.ordem, item.id]);
|
||||||
}
|
}
|
||||||
await connection.commit();
|
});
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await connection.rollback();
|
|
||||||
res.status(500).json({ error: err.message });
|
res.status(500).json({ error: err.message });
|
||||||
} finally { connection.release(); }
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.put('/api/especialidades/:id', async (req, res) => {
|
app.put('/api/especialidades/:id', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await pool.query('UPDATE especialidades SET ? WHERE id = ?', [req.body, req.params.id]);
|
const upd = buildUpdate('especialidades', req.body, 'id', req.params.id);
|
||||||
|
await pool.query(upd.text, upd.values);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
app.delete('/api/especialidades/:id', async (req, res) => {
|
app.delete('/api/especialidades/:id', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await pool.query('DELETE FROM especialidades WHERE id = ?', [req.params.id]);
|
await pool.query('DELETE FROM especialidades WHERE id = $1', [req.params.id]);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
@@ -905,28 +810,30 @@ app.delete('/api/especialidades/:id', async (req, res) => {
|
|||||||
// --- PLANOS ---
|
// --- PLANOS ---
|
||||||
app.get('/api/planos', async (req, res) => {
|
app.get('/api/planos', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const [rows] = await pool.query('SELECT * FROM planos');
|
const { rows } = await pool.query('SELECT * FROM planos');
|
||||||
res.json(rows);
|
res.json(rows);
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/api/planos', async (req, res) => {
|
app.post('/api/planos', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await pool.query('INSERT INTO planos SET ?', req.body);
|
const ins = buildInsert('planos', req.body);
|
||||||
|
await pool.query(ins.text, ins.values);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
app.put('/api/planos/:id', async (req, res) => {
|
app.put('/api/planos/:id', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await pool.query('UPDATE planos SET ? WHERE id = ?', [req.body, req.params.id]);
|
const upd = buildUpdate('planos', req.body, 'id', req.params.id);
|
||||||
|
await pool.query(upd.text, upd.values);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
app.delete('/api/planos/:id', async (req, res) => {
|
app.delete('/api/planos/:id', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await pool.query('DELETE FROM planos WHERE id = ?', [req.params.id]);
|
await pool.query('DELETE FROM planos WHERE id = $1', [req.params.id]);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
@@ -934,8 +841,8 @@ app.delete('/api/planos/:id', async (req, res) => {
|
|||||||
// --- PROCEDIMENTOS ---
|
// --- PROCEDIMENTOS ---
|
||||||
app.get('/api/procedimentos', async (req, res) => {
|
app.get('/api/procedimentos', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const [procs] = await pool.query('SELECT * FROM procedimentos ORDER BY ordem ASC, nome ASC');
|
const { rows: procs } = await pool.query('SELECT * FROM procedimentos ORDER BY ordem ASC, nome ASC');
|
||||||
const [valores] = await pool.query('SELECT * FROM procedimentos_valores_planos');
|
const { rows: valores } = await pool.query('SELECT * FROM procedimentos_valores_planos');
|
||||||
|
|
||||||
const data = procs.map(p => ({
|
const data = procs.map(p => ({
|
||||||
...p,
|
...p,
|
||||||
@@ -949,21 +856,19 @@ app.get('/api/procedimentos', async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.post('/api/procedimentos', async (req, res) => {
|
app.post('/api/procedimentos', async (req, res) => {
|
||||||
const connection = await pool.getConnection();
|
|
||||||
try {
|
try {
|
||||||
await connection.beginTransaction();
|
|
||||||
const { valoresPlanos, ...proc } = req.body;
|
const { valoresPlanos, ...proc } = req.body;
|
||||||
|
|
||||||
// Auto-generate codigoInterno for PARTICULAR
|
// Auto-generate codigoInterno for PARTICULAR
|
||||||
let codigoInternoVal = proc.codigoInterno;
|
let codigoInternoVal = proc.codigoInterno;
|
||||||
if (proc.categoria === 'Particular' && (!codigoInternoVal || codigoInternoVal === '')) {
|
if (proc.categoria === 'Particular' && (!codigoInternoVal || codigoInternoVal === '')) {
|
||||||
const [rows] = await connection.query('SELECT COUNT(*) as count FROM procedimentos WHERE categoria = "Particular"');
|
const { rows: partCount } = await pool.query("SELECT COUNT(*) as count FROM procedimentos WHERE categoria = 'Particular'");
|
||||||
codigoInternoVal = (rows[0].count + 1).toString();
|
codigoInternoVal = (parseInt(partCount[0].count) + 1).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Automatically set order to the end
|
// Automatically set order to the end
|
||||||
const [counts] = await connection.query('SELECT COUNT(*) as count FROM procedimentos');
|
const { rows: counts } = await pool.query('SELECT COUNT(*) as count FROM procedimentos');
|
||||||
const ordemValue = counts[0].count;
|
const ordemValue = parseInt(counts[0].count);
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
...proc,
|
...proc,
|
||||||
@@ -972,68 +877,63 @@ app.post('/api/procedimentos', async (req, res) => {
|
|||||||
tipo_regiao: proc.tipo_regiao || 'GERAL',
|
tipo_regiao: proc.tipo_regiao || 'GERAL',
|
||||||
exige_face: proc.exige_face ? 1 : 0
|
exige_face: proc.exige_face ? 1 : 0
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!payload.id) payload.id = Math.random().toString(36).substring(2, 10).toUpperCase();
|
if (!payload.id) payload.id = Math.random().toString(36).substring(2, 10).toUpperCase();
|
||||||
|
|
||||||
await connection.query('INSERT INTO procedimentos SET ?', payload);
|
await withTransaction(async (client) => {
|
||||||
|
const ins = buildInsert('procedimentos', payload);
|
||||||
|
await client.query(ins.text, ins.values);
|
||||||
if (valoresPlanos && valoresPlanos.length > 0) {
|
if (valoresPlanos && valoresPlanos.length > 0) {
|
||||||
for (const v of valoresPlanos) {
|
for (const v of valoresPlanos) {
|
||||||
await connection.query('INSERT INTO procedimentos_valores_planos SET ?', { ...v, procedimentoId: payload.id });
|
const vIns = buildInsert('procedimentos_valores_planos', { ...v, procedimentoId: payload.id });
|
||||||
|
await client.query(vIns.text, vIns.values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await connection.commit();
|
});
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await connection.rollback();
|
|
||||||
res.status(500).json({ error: err.message });
|
res.status(500).json({ error: err.message });
|
||||||
} finally { connection.release(); }
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.put('/api/procedimentos/:id', async (req, res) => {
|
app.put('/api/procedimentos/:id', async (req, res) => {
|
||||||
const connection = await pool.getConnection();
|
|
||||||
try {
|
try {
|
||||||
await connection.beginTransaction();
|
|
||||||
const { valoresPlanos, ...proc } = req.body;
|
const { valoresPlanos, ...proc } = req.body;
|
||||||
|
const updateData = { ...proc, exige_face: proc.exige_face ? 1 : 0 };
|
||||||
|
|
||||||
const updateData = {
|
await withTransaction(async (client) => {
|
||||||
...proc,
|
const upd = buildUpdate('procedimentos', updateData, 'id', req.params.id);
|
||||||
exige_face: proc.exige_face ? 1 : 0
|
await client.query(upd.text, upd.values);
|
||||||
};
|
await client.query('DELETE FROM procedimentos_valores_planos WHERE "procedimentoId" = $1', [req.params.id]);
|
||||||
|
|
||||||
await connection.query('UPDATE procedimentos SET ? WHERE id = ?', [updateData, req.params.id]);
|
|
||||||
await connection.query('DELETE FROM procedimentos_valores_planos WHERE procedimentoId = ?', [req.params.id]);
|
|
||||||
if (valoresPlanos && valoresPlanos.length > 0) {
|
if (valoresPlanos && valoresPlanos.length > 0) {
|
||||||
for (const v of valoresPlanos) {
|
for (const v of valoresPlanos) {
|
||||||
await connection.query('INSERT INTO procedimentos_valores_planos SET ?', { ...v, procedimentoId: req.params.id });
|
const vIns = buildInsert('procedimentos_valores_planos', { ...v, procedimentoId: req.params.id });
|
||||||
|
await client.query(vIns.text, vIns.values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await connection.commit();
|
});
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await connection.rollback();
|
|
||||||
res.status(500).json({ error: err.message });
|
res.status(500).json({ error: err.message });
|
||||||
} finally { connection.release(); }
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.put('/api/procedimentos/reorder', async (req, res) => {
|
app.put('/api/procedimentos/reorder', async (req, res) => {
|
||||||
const { orders } = req.body; // Array of {id, ordem}
|
const { orders } = req.body; // Array of {id, ordem}
|
||||||
const connection = await pool.getConnection();
|
|
||||||
try {
|
try {
|
||||||
await connection.beginTransaction();
|
await withTransaction(async (client) => {
|
||||||
for (const item of orders) {
|
for (const item of orders) {
|
||||||
await connection.query('UPDATE procedimentos SET ordem = ? WHERE id = ?', [item.ordem, item.id]);
|
await client.query('UPDATE procedimentos SET ordem = $1 WHERE id = $2', [item.ordem, item.id]);
|
||||||
}
|
}
|
||||||
await connection.commit();
|
});
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await connection.rollback();
|
|
||||||
res.status(500).json({ error: err.message });
|
res.status(500).json({ error: err.message });
|
||||||
} finally { connection.release(); }
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.delete('/api/procedimentos/:id', async (req, res) => {
|
app.delete('/api/procedimentos/:id', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await pool.query('DELETE FROM procedimentos WHERE id = ?', [req.params.id]);
|
await pool.query('DELETE FROM procedimentos WHERE id = $1', [req.params.id]);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
@@ -1041,28 +941,30 @@ app.delete('/api/procedimentos/:id', async (req, res) => {
|
|||||||
// --- FINANCEIRO ---
|
// --- FINANCEIRO ---
|
||||||
app.get('/api/financeiro', async (req, res) => {
|
app.get('/api/financeiro', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const [rows] = await pool.query('SELECT * FROM financeiro');
|
const { rows } = await pool.query('SELECT * FROM financeiro');
|
||||||
res.json(rows.map(r => ({ ...r, valor: parseFloat(r.valor || 0) })));
|
res.json(rows.map(r => ({ ...r, valor: parseFloat(r.valor || 0) })));
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/api/financeiro', async (req, res) => {
|
app.post('/api/financeiro', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await pool.query('INSERT INTO financeiro SET ?', req.body);
|
const ins = buildInsert('financeiro', req.body);
|
||||||
|
await pool.query(ins.text, ins.values);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
app.put('/api/financeiro/:id', async (req, res) => {
|
app.put('/api/financeiro/:id', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await pool.query('UPDATE financeiro SET ? WHERE id = ?', [req.body, req.params.id]);
|
const upd = buildUpdate('financeiro', req.body, 'id', req.params.id);
|
||||||
|
await pool.query(upd.text, upd.values);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
app.delete('/api/financeiro/:id', async (req, res) => {
|
app.delete('/api/financeiro/:id', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await pool.query('DELETE FROM financeiro WHERE id = ?', [req.params.id]);
|
await pool.query('DELETE FROM financeiro WHERE id = $1', [req.params.id]);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
@@ -1070,14 +972,15 @@ app.delete('/api/financeiro/:id', async (req, res) => {
|
|||||||
// --- LEADS ---
|
// --- LEADS ---
|
||||||
app.get('/api/leads', async (req, res) => {
|
app.get('/api/leads', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const [rows] = await pool.query('SELECT * FROM leads');
|
const { rows } = await pool.query('SELECT * FROM leads');
|
||||||
res.json(rows);
|
res.json(rows);
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/api/leads', async (req, res) => {
|
app.post('/api/leads', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await pool.query('INSERT INTO leads SET ?', req.body);
|
const ins = buildInsert('leads', req.body);
|
||||||
|
await pool.query(ins.text, ins.values);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
@@ -1102,48 +1005,54 @@ app.get('/api/guias', async (req, res) => {
|
|||||||
|
|
||||||
let where = ' WHERE 1=1';
|
let where = ' WHERE 1=1';
|
||||||
const params = [];
|
const params = [];
|
||||||
|
let paramIdx = 1;
|
||||||
|
|
||||||
if (gto) {
|
if (gto) {
|
||||||
where += ' AND (numeroGuiaPrestador LIKE ? OR numeroGuiaOperadora LIKE ?)';
|
where += ` AND ("numeroGuiaPrestador" LIKE $${paramIdx} OR "numeroGuiaOperadora" LIKE $${paramIdx + 1})`;
|
||||||
params.push(`%${gto}%`, `%${gto}%`);
|
params.push(`%${gto}%`, `%${gto}%`);
|
||||||
|
paramIdx += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (search) {
|
if (search) {
|
||||||
where += ' AND (beneficiarioNome LIKE ? OR beneficiarioIdentificacao LIKE ?)';
|
where += ` AND ("beneficiarioNome" LIKE $${paramIdx} OR "beneficiarioIdentificacao" LIKE $${paramIdx + 1})`;
|
||||||
params.push(`%${search}%`, `%${search}%`);
|
params.push(`%${search}%`, `%${search}%`);
|
||||||
|
paramIdx += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
where += ' AND status = ?';
|
where += ` AND status = $${paramIdx}`;
|
||||||
params.push(status);
|
params.push(status);
|
||||||
|
paramIdx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataInicio) {
|
if (dataInicio) {
|
||||||
where += ' AND dataSolicitacao >= ?';
|
where += ` AND "dataSolicitacao" >= $${paramIdx}`;
|
||||||
params.push(dataInicio);
|
params.push(dataInicio);
|
||||||
|
paramIdx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataFim) {
|
if (dataFim) {
|
||||||
where += ' AND dataSolicitacao <= ?';
|
where += ` AND "dataSolicitacao" <= $${paramIdx}`;
|
||||||
params.push(dataFim);
|
params.push(dataFim);
|
||||||
|
paramIdx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate sortField and sortOrder to prevent SQL injection
|
// Validate sortField and sortOrder to prevent SQL injection
|
||||||
const allowedSortFields = ['dataSolicitacao', 'beneficiarioNome', 'status', 'numeroGuiaPrestador'];
|
const allowedSortFields = ['dataSolicitacao', 'beneficiarioNome', 'status', 'numeroGuiaPrestador'];
|
||||||
const finalSortField = allowedSortFields.includes(sortField) ? sortField : 'dataSolicitacao';
|
const finalSortField = allowedSortFields.includes(sortField) ? `"${sortField}"` : '"dataSolicitacao"';
|
||||||
const finalSortOrder = sortOrder.toUpperCase() === 'ASC' ? 'ASC' : 'DESC';
|
const finalSortOrder = sortOrder.toUpperCase() === 'ASC' ? 'ASC' : 'DESC';
|
||||||
|
|
||||||
const query = `
|
const query = `
|
||||||
SELECT * FROM guias_odontologicas
|
SELECT * FROM guias_odontologicas
|
||||||
${where}
|
${where}
|
||||||
ORDER BY ${finalSortField} ${finalSortOrder}
|
ORDER BY ${finalSortField} ${finalSortOrder}
|
||||||
LIMIT ? OFFSET ?
|
LIMIT $${paramIdx} OFFSET $${paramIdx + 1}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const countQuery = `SELECT COUNT(*) as total FROM guias_odontologicas ${where}`;
|
const countQuery = `SELECT COUNT(*) as total FROM guias_odontologicas ${where}`;
|
||||||
|
|
||||||
const [rows] = await pool.query(query, [...params, limit, offset]);
|
const { rows } = await pool.query(query, [...params, limit, offset]);
|
||||||
const [totalRows] = await pool.query(countQuery, params);
|
const { rows: totalRows } = await pool.query(countQuery, params);
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
data: rows,
|
data: rows,
|
||||||
@@ -1162,7 +1071,7 @@ app.post('/api/gto-builder', async (req, res) => {
|
|||||||
const { pacienteId, dentistaId, credenciadaId } = req.body;
|
const { pacienteId, dentistaId, credenciadaId } = req.body;
|
||||||
const id = Math.random().toString(36).substring(2, 9).toUpperCase();
|
const id = Math.random().toString(36).substring(2, 9).toUpperCase();
|
||||||
await pool.query(
|
await pool.query(
|
||||||
"INSERT INTO gto_builders (id, pacienteId, dentistaId, credenciadaId, status, total) VALUES ($1, $2, $3, $4, 'RASCUNHO', 0)",
|
"INSERT INTO gto_builders (id, \"pacienteId\", \"dentistaId\", \"credenciadaId\", status, total) VALUES ($1, $2, $3, $4, 'RASCUNHO', 0)",
|
||||||
[id, pacienteId, dentistaId, credenciadaId]
|
[id, pacienteId, dentistaId, credenciadaId]
|
||||||
);
|
);
|
||||||
res.json({ id });
|
res.json({ id });
|
||||||
@@ -1171,8 +1080,8 @@ app.post('/api/gto-builder', async (req, res) => {
|
|||||||
|
|
||||||
app.get('/api/gto-builder/:id', async (req, res) => {
|
app.get('/api/gto-builder/:id', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const [builder] = await pool.query('SELECT * FROM gto_builders WHERE id = ?', [req.params.id]);
|
const { rows: builder } = await pool.query('SELECT * FROM gto_builders WHERE id = $1', [req.params.id]);
|
||||||
const [items] = await pool.query('SELECT * FROM gto_items WHERE builderId = ?', [req.params.id]);
|
const { rows: items } = await pool.query('SELECT * FROM gto_items WHERE "builderId" = $1', [req.params.id]);
|
||||||
res.json({ ...builder[0], items });
|
res.json({ ...builder[0], items });
|
||||||
} catch (err) { res.status(500).json({ error: err.message }); }
|
} catch (err) { res.status(500).json({ error: err.message }); }
|
||||||
});
|
});
|
||||||
@@ -1181,11 +1090,12 @@ app.post('/api/gto-builder/:id/item', async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const itemId = Math.random().toString(36).substring(2, 9).toUpperCase();
|
const itemId = Math.random().toString(36).substring(2, 9).toUpperCase();
|
||||||
const item = { ...req.body, id: itemId, builderId: req.params.id };
|
const item = { ...req.body, id: itemId, builderId: req.params.id };
|
||||||
await pool.query('INSERT INTO gto_items SET ?', item);
|
const ins = buildInsert('gto_items', item);
|
||||||
|
await pool.query(ins.text, ins.values);
|
||||||
|
|
||||||
// Update total
|
// Update total
|
||||||
await pool.query(
|
await pool.query(
|
||||||
'UPDATE gto_builders SET total = (SELECT SUM(valorTotal) FROM gto_items WHERE builderId = ?) WHERE id = ?',
|
'UPDATE gto_builders SET total = (SELECT SUM("valorTotal") FROM gto_items WHERE "builderId" = $1) WHERE id = $2',
|
||||||
[req.params.id, req.params.id]
|
[req.params.id, req.params.id]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1195,10 +1105,10 @@ app.post('/api/gto-builder/:id/item', async (req, res) => {
|
|||||||
|
|
||||||
app.delete('/api/gto-builder/:id/item/:itemId', async (req, res) => {
|
app.delete('/api/gto-builder/:id/item/:itemId', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
await pool.query('DELETE FROM gto_items WHERE id = ?', [req.params.itemId]);
|
await pool.query('DELETE FROM gto_items WHERE id = $1', [req.params.itemId]);
|
||||||
// Update total
|
// Update total
|
||||||
await pool.query(
|
await pool.query(
|
||||||
'UPDATE gto_builders SET total = COALESCE((SELECT SUM(valorTotal) FROM gto_items WHERE builderId = ?), 0) WHERE id = ?',
|
'UPDATE gto_builders SET total = COALESCE((SELECT SUM("valorTotal") FROM gto_items WHERE "builderId" = $1), 0) WHERE id = $2',
|
||||||
[req.params.id, req.params.id]
|
[req.params.id, req.params.id]
|
||||||
);
|
);
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
@@ -1307,4 +1217,3 @@ app.listen(PORT, '0.0.0.0', () => {
|
|||||||
generateIntelligentNotifications();
|
generateIntelligentNotifications();
|
||||||
setInterval(generateIntelligentNotifications, 5 * 60 * 1000);
|
setInterval(generateIntelligentNotifications, 5 * 60 * 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user