Actualizar server.js
This commit is contained in:
86
server.js
86
server.js
@@ -1039,57 +1039,7 @@ app.post("/providers/automate/:id", authMiddleware, async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// AÑADIDO: CAPTURA COMPLETA DE DATOS (...EXTRA)
|
// AÑADIDO Y BLINDADO: CAPTURA COMPLETA, REGLA WA MANUAL, DESASIGNACIÓN Y BORRADO DE FECHA
|
||||||
app.post("/providers/import/:id", authMiddleware, async (req, res) => {
|
|
||||||
const client = await pool.connect();
|
|
||||||
try {
|
|
||||||
const scrapedId = req.params.id;
|
|
||||||
const { name, phone, address, cp, description, guild_id, assigned_to, internal_notes, client_notes, is_urgent, ...extra } = req.body;
|
|
||||||
|
|
||||||
await client.query('BEGIN');
|
|
||||||
const scrapedQ = await client.query("SELECT * FROM scraped_services WHERE id=$1 AND owner_id=$2", [scrapedId, req.user.accountId]);
|
|
||||||
if (scrapedQ.rowCount === 0) return res.status(404).json({ ok: false });
|
|
||||||
|
|
||||||
const raw = scrapedQ.rows[0].raw_data;
|
|
||||||
const provider = scrapedQ.rows[0].provider;
|
|
||||||
const ref = scrapedQ.rows[0].service_ref;
|
|
||||||
|
|
||||||
const phoneClean = normalizePhone(phone);
|
|
||||||
let clientId = null;
|
|
||||||
if (phoneClean) {
|
|
||||||
const cCheck = await client.query("SELECT id FROM clients WHERE phone=$1 AND owner_id=$2", [phoneClean, req.user.accountId]);
|
|
||||||
if (cCheck.rowCount > 0) clientId = cCheck.rows[0].id;
|
|
||||||
}
|
|
||||||
if (!clientId) {
|
|
||||||
const newC = await client.query("INSERT INTO clients (owner_id, full_name, phone, addresses) VALUES ($1, $2, $3, $4) RETURNING id", [req.user.accountId, name, phoneClean, JSON.stringify([address])]);
|
|
||||||
clientId = newC.rows[0].id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const statusQ = await client.query("SELECT id FROM service_statuses WHERE owner_id=$1 AND is_default=TRUE LIMIT 1", [req.user.accountId]);
|
|
||||||
const finalStatusId = statusQ.rows[0]?.id;
|
|
||||||
|
|
||||||
// Fusión total: datos originales del scraper + todo lo recibido en el body
|
|
||||||
const fullProviderData = { ...raw, ...extra, "Nombre Cliente": name, "Teléfono": phone, "Dirección": address, "Código Postal": cp, "Descripción": description };
|
|
||||||
|
|
||||||
const insertSvc = await client.query(`
|
|
||||||
INSERT INTO services
|
|
||||||
(owner_id, client_id, status_id, company_ref, title, description, address, contact_phone, contact_name, is_company, import_source, provider_data, guild_id, assigned_to, internal_notes, client_notes, is_urgent)
|
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
|
|
||||||
RETURNING id`,
|
|
||||||
[req.user.accountId, clientId, finalStatusId, ref, `${provider.toUpperCase()} - ${ref}`, description, address, phoneClean, name, true, provider, JSON.stringify(fullProviderData), guild_id || null, assigned_to || null, internal_notes, client_notes, is_urgent]
|
|
||||||
);
|
|
||||||
|
|
||||||
await client.query("UPDATE scraped_services SET status='imported' WHERE id=$1", [scrapedId]);
|
|
||||||
await client.query('COMMIT');
|
|
||||||
res.json({ ok: true, serviceId: insertSvc.rows[0].id, assigned: !!assigned_to });
|
|
||||||
} catch (e) {
|
|
||||||
await client.query('ROLLBACK');
|
|
||||||
res.status(500).json({ ok: false });
|
|
||||||
} finally { client.release(); }
|
|
||||||
});
|
|
||||||
|
|
||||||
// AÑADIDO Y BLINDADO: CAPTURA COMPLETA DE DATOS Y REGLA WHATSAPP MANUAL
|
|
||||||
// AÑADIDO Y BLINDADO: CAPTURA COMPLETA DE DATOS, REGLA WHATSAPP MANUAL Y DESASIGNACIÓN
|
|
||||||
app.put('/providers/scraped/:id', authMiddleware, async (req, res) => {
|
app.put('/providers/scraped/:id', authMiddleware, async (req, res) => {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
let { automation_status, status, name, phone, address, cp, description, guild_id, assigned_to, assigned_to_name, internal_notes, client_notes, is_urgent, ...extra } = req.body;
|
let { automation_status, status, name, phone, address, cp, description, guild_id, assigned_to, assigned_to_name, internal_notes, client_notes, is_urgent, ...extra } = req.body;
|
||||||
@@ -1108,18 +1058,21 @@ app.put('/providers/scraped/:id', authMiddleware, async (req, res) => {
|
|||||||
const current = await pool.query('SELECT raw_data, assigned_to, status FROM scraped_services WHERE id = $1 AND owner_id = $2', [id, req.user.accountId]);
|
const current = await pool.query('SELECT raw_data, assigned_to, status FROM scraped_services WHERE id = $1 AND owner_id = $2', [id, req.user.accountId]);
|
||||||
if (current.rows.length === 0) return res.status(404).json({ error: 'No encontrado' });
|
if (current.rows.length === 0) return res.status(404).json({ error: 'No encontrado' });
|
||||||
|
|
||||||
// --- BLINDAJE DE ESTADO VACÍO ---
|
|
||||||
let oldStatus = current.rows[0].raw_data.status_operativo || null;
|
let oldStatus = current.rows[0].raw_data.status_operativo || null;
|
||||||
let newStatus = extra.status_operativo || oldStatus;
|
let newStatus = extra.status_operativo || oldStatus;
|
||||||
if (newStatus === "") newStatus = null;
|
if (newStatus === "") newStatus = null;
|
||||||
|
|
||||||
// --- REGLAS DE CAMBIO DE ESTADO MANUAL ---
|
|
||||||
if (newStatus && newStatus !== oldStatus) {
|
if (newStatus && newStatus !== oldStatus) {
|
||||||
const statusQ = await pool.query("SELECT name FROM service_statuses WHERE id=$1", [newStatus]);
|
const statusQ = await pool.query("SELECT name FROM service_statuses WHERE id=$1", [newStatus]);
|
||||||
const stName = (statusQ.rows[0]?.name || "").toLowerCase();
|
const stName = (statusQ.rows[0]?.name || "").toLowerCase();
|
||||||
|
|
||||||
|
// --- NUEVA REGLA: BORRADO DE FECHA SI RETROCEDE O SE ANULA ---
|
||||||
|
if (stName.includes('pendiente') || stName.includes('desasignado') || stName.includes('asignado') || stName.includes('anulado') || stName.includes('esperando')) {
|
||||||
|
extra.scheduled_date = "";
|
||||||
|
extra.scheduled_time = "";
|
||||||
|
}
|
||||||
|
|
||||||
if (stName.includes('asignado')) {
|
if (stName.includes('asignado')) {
|
||||||
// Regla Asignado -> Pasa a Esperando si se envía WA
|
|
||||||
const waEnviadoExito = await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_assigned');
|
const waEnviadoExito = await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_assigned');
|
||||||
if (waEnviadoExito) {
|
if (waEnviadoExito) {
|
||||||
const estadoEsperando = await pool.query("SELECT id FROM service_statuses WHERE owner_id=$1 AND name='Esperando al Cliente' LIMIT 1", [req.user.accountId]);
|
const estadoEsperando = await pool.query("SELECT id FROM service_statuses WHERE owner_id=$1 AND name='Esperando al Cliente' LIMIT 1", [req.user.accountId]);
|
||||||
@@ -1129,7 +1082,6 @@ app.put('/providers/scraped/:id', authMiddleware, async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (stName.includes('pendiente de asignar') || stName.includes('desasignado')) {
|
} else if (stName.includes('pendiente de asignar') || stName.includes('desasignado')) {
|
||||||
// Regla Desasignar -> Avisa al Operario y borra datos
|
|
||||||
const oldWorkerId = current.rows[0].assigned_to || current.rows[0].raw_data.assigned_to;
|
const oldWorkerId = current.rows[0].assigned_to || current.rows[0].raw_data.assigned_to;
|
||||||
if (oldWorkerId) {
|
if (oldWorkerId) {
|
||||||
const workerQ = await pool.query("SELECT full_name, phone FROM users WHERE id=$1", [oldWorkerId]);
|
const workerQ = await pool.query("SELECT full_name, phone FROM users WHERE id=$1", [oldWorkerId]);
|
||||||
@@ -1140,7 +1092,6 @@ app.put('/providers/scraped/:id', authMiddleware, async (req, res) => {
|
|||||||
sendWhatsAppAuto(w.phone, msg, `cliente_${req.user.accountId}`, false).catch(console.error);
|
sendWhatsAppAuto(w.phone, msg, `cliente_${req.user.accountId}`, false).catch(console.error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Forzamos el borrado del operario
|
|
||||||
assigned_to = null;
|
assigned_to = null;
|
||||||
assigned_to_name = null;
|
assigned_to_name = null;
|
||||||
extra.assigned_to = null;
|
extra.assigned_to = null;
|
||||||
@@ -1165,10 +1116,9 @@ app.put('/providers/scraped/:id', authMiddleware, async (req, res) => {
|
|||||||
"status_operativo": newStatus
|
"status_operativo": newStatus
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- BLINDAJE DE OPERARIO VACÍO (Evita el error 500) ---
|
|
||||||
let finalAssignedTo = assigned_to;
|
let finalAssignedTo = assigned_to;
|
||||||
if (finalAssignedTo === "" || finalAssignedTo === null) finalAssignedTo = null; // Queda libre
|
if (finalAssignedTo === "" || finalAssignedTo === null) finalAssignedTo = null;
|
||||||
else if (finalAssignedTo === undefined) finalAssignedTo = current.rows[0].assigned_to; // Mantiene el que tenía
|
else if (finalAssignedTo === undefined) finalAssignedTo = current.rows[0].assigned_to;
|
||||||
|
|
||||||
let currentDbStatus = current.rows[0].status;
|
let currentDbStatus = current.rows[0].status;
|
||||||
|
|
||||||
@@ -1224,17 +1174,15 @@ app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => {
|
|||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
let { date, time, status_operativo, ...extra } = req.body;
|
let { date, time, status_operativo, ...extra } = req.body;
|
||||||
|
|
||||||
// REPARADO: Añadido assigned_to al SELECT para poder avisar al operario
|
|
||||||
const current = await pool.query('SELECT raw_data, assigned_to FROM scraped_services WHERE id = $1 AND owner_id = $2', [id, req.user.accountId]);
|
const current = await pool.query('SELECT raw_data, assigned_to FROM scraped_services WHERE id = $1 AND owner_id = $2', [id, req.user.accountId]);
|
||||||
if (current.rowCount === 0) return res.status(404).json({ ok: false, error: 'No encontrado' });
|
if (current.rowCount === 0) return res.status(404).json({ ok: false, error: 'No encontrado' });
|
||||||
|
|
||||||
const oldDate = current.rows[0].raw_data.scheduled_date || "";
|
const oldDate = current.rows[0].raw_data.scheduled_date || "";
|
||||||
const oldTime = current.rows[0].raw_data.scheduled_time || "";
|
const oldTime = current.rows[0].raw_data.scheduled_time || "";
|
||||||
const newDate = date || "";
|
let newDate = date || "";
|
||||||
const newTime = time || "";
|
let newTime = time || "";
|
||||||
let finalAssignedTo = current.rows[0].assigned_to; // Lo guardamos por defecto
|
let finalAssignedTo = current.rows[0].assigned_to;
|
||||||
|
|
||||||
// BLINDAJE DE ESTADO VACÍO
|
|
||||||
if (status_operativo === "") status_operativo = null;
|
if (status_operativo === "") status_operativo = null;
|
||||||
|
|
||||||
let stName = "";
|
let stName = "";
|
||||||
@@ -1243,6 +1191,12 @@ app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => {
|
|||||||
stName = (statusQ.rows[0]?.name || "").toLowerCase();
|
stName = (statusQ.rows[0]?.name || "").toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- NUEVA REGLA: BORRAR FECHAS SI SE ANULA O RETROCEDE ---
|
||||||
|
if (stName.includes('pendiente') || stName.includes('desasignado') || stName.includes('asignado') || stName.includes('anulado') || stName.includes('esperando')) {
|
||||||
|
newDate = "";
|
||||||
|
newTime = "";
|
||||||
|
}
|
||||||
|
|
||||||
// --- MOTOR DE EVENTOS Y DESASIGNACIÓN ---
|
// --- MOTOR DE EVENTOS Y DESASIGNACIÓN ---
|
||||||
if (stName.includes('asignado')) {
|
if (stName.includes('asignado')) {
|
||||||
const waEnviadoExito = await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_assigned');
|
const waEnviadoExito = await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_assigned');
|
||||||
@@ -1254,7 +1208,6 @@ app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (stName.includes('pendiente de asignar') || stName.includes('desasignado')) {
|
else if (stName.includes('pendiente de asignar') || stName.includes('desasignado')) {
|
||||||
// DESASIGNAR: Notificar al operario y borrar rastro
|
|
||||||
const oldWorkerId = finalAssignedTo || current.rows[0].raw_data.assigned_to;
|
const oldWorkerId = finalAssignedTo || current.rows[0].raw_data.assigned_to;
|
||||||
if (oldWorkerId) {
|
if (oldWorkerId) {
|
||||||
const workerQ = await pool.query("SELECT full_name, phone FROM users WHERE id=$1", [oldWorkerId]);
|
const workerQ = await pool.query("SELECT full_name, phone FROM users WHERE id=$1", [oldWorkerId]);
|
||||||
@@ -1267,7 +1220,7 @@ app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => {
|
|||||||
}
|
}
|
||||||
extra.assigned_to = null;
|
extra.assigned_to = null;
|
||||||
extra.assigned_to_name = null;
|
extra.assigned_to_name = null;
|
||||||
finalAssignedTo = null; // Borramos el físico también
|
finalAssignedTo = null;
|
||||||
}
|
}
|
||||||
else if (stName.includes('citado') && newDate !== "") {
|
else if (stName.includes('citado') && newDate !== "") {
|
||||||
if (oldDate === "") await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_date');
|
if (oldDate === "") await triggerWhatsAppEvent(req.user.accountId, id, 'wa_evt_date');
|
||||||
@@ -1280,7 +1233,6 @@ app.put("/services/set-appointment/:id", authMiddleware, async (req, res) => {
|
|||||||
|
|
||||||
const updatedRawData = { ...current.rows[0].raw_data, ...extra, "scheduled_date": newDate, "scheduled_time": newTime, "status_operativo": status_operativo };
|
const updatedRawData = { ...current.rows[0].raw_data, ...extra, "scheduled_date": newDate, "scheduled_time": newTime, "status_operativo": status_operativo };
|
||||||
|
|
||||||
// ACTUALIZAMOS RAW DATA Y EL OPERARIO FÍSICO
|
|
||||||
await pool.query('UPDATE scraped_services SET raw_data = $1, assigned_to = $2 WHERE id = $3 AND owner_id = $4', [JSON.stringify(updatedRawData), finalAssignedTo, id, req.user.accountId]);
|
await pool.query('UPDATE scraped_services SET raw_data = $1, assigned_to = $2 WHERE id = $3 AND owner_id = $4', [JSON.stringify(updatedRawData), finalAssignedTo, id, req.user.accountId]);
|
||||||
|
|
||||||
res.json({ ok: true });
|
res.json({ ok: true });
|
||||||
|
|||||||
Reference in New Issue
Block a user