Adiciona Fluxo de Resolução (motivo + resolução do atendimento)
- Settings > Fluxo: cadastro de motivos e flags (visualizar/obrigatório para motivo e resolução) - Tela de conversa: dropdown de motivos + campo de resolução abaixo dos dados do titular, exibidos conforme configuração - Finalização valida obrigatoriedade (front e backend) e salva CON_MOTIVO_ID/CON_RESOLUCAO - Estrutura criada de forma idempotente (Postgres/Firebird) em src/resolucaoSetup.js; CRUD de motivos restrito a gerente Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
const db = require('../database');
|
||||
const { garantirEstrutura } = require('../resolucaoSetup');
|
||||
const ffmpegPath = require('ffmpeg-static');
|
||||
const { execFile } = require('child_process');
|
||||
const path = require('path');
|
||||
@@ -446,6 +447,8 @@ class ChatController {
|
||||
saudacaoEnviada: (row.CON_SAUDACAO_ENVIADA || 'N').trim(),
|
||||
csatEnviado: (row.CON_CSAT_ENVIADO || 'N').trim(),
|
||||
primeiraMsg: row.CON_PRIMEIRA_MSG,
|
||||
motivoId: row.CON_MOTIVO_ID || null,
|
||||
resolucao: row.CON_RESOLUCAO || null,
|
||||
cliente: clienteInfo,
|
||||
dependente: dependenteInfo,
|
||||
labels: labels.map(l => ({ id: l.ETI_CODIGO_ID, nome: (l.ETI_NOME || '').trim(), cor: (l.ETI_COR || '#667eea').trim() })),
|
||||
@@ -662,6 +665,23 @@ class ChatController {
|
||||
if (c.CON_EMPRESA_ID && !((req.user && req.user.empresas) || []).includes(c.CON_EMPRESA_ID))
|
||||
return res.status(403).json({ success: false, error: 'Sem permissão.' });
|
||||
|
||||
// ===== Fluxo de Resolução: valida motivo/resolução conforme config =====
|
||||
await garantirEstrutura(alias);
|
||||
const motivoId = (req.body && req.body.motivoId) ? parseInt(req.body.motivoId, 10) : null;
|
||||
const resolucao = (req.body && req.body.resolucao != null) ? String(req.body.resolucao).trim() : '';
|
||||
|
||||
const flagsRows = await db.query(alias,
|
||||
'SELECT CFE_MOTIVO_VISUALIZAR, CFE_MOTIVO_OBRIGATORIO, CFE_RESOLUCAO_VISUALIZAR, CFE_RESOLUCAO_OBRIGATORIO FROM CHATC2_CONFIGURACOES_EMPRESA WHERE CFE_EMPRESA_ID = ?',
|
||||
[c.CON_EMPRESA_ID]);
|
||||
const fl = flagsRows[0] || {};
|
||||
const isS = (v) => String(v || 'N').trim() === 'S';
|
||||
if (isS(fl.CFE_MOTIVO_VISUALIZAR) && isS(fl.CFE_MOTIVO_OBRIGATORIO) && !motivoId) {
|
||||
return res.status(400).json({ success: false, error: 'Selecione o motivo do atendimento para finalizar.' });
|
||||
}
|
||||
if (isS(fl.CFE_RESOLUCAO_VISUALIZAR) && isS(fl.CFE_RESOLUCAO_OBRIGATORIO) && !resolucao) {
|
||||
return res.status(400).json({ success: false, error: 'Preencha a resolução do atendimento para finalizar.' });
|
||||
}
|
||||
|
||||
// Busca nome do usuário que atendeu
|
||||
let usuarioNome = null;
|
||||
if (c.CON_USUARIO_ID) {
|
||||
@@ -692,9 +712,11 @@ class ChatController {
|
||||
CON_DT_FINAL = CURRENT_TIMESTAMP,
|
||||
CON_USUARIO_NOME = ?,
|
||||
CON_EQUIPE_NOME = ?,
|
||||
CON_ETIQUETAS_DESC = ?
|
||||
CON_ETIQUETAS_DESC = ?,
|
||||
CON_MOTIVO_ID = ?,
|
||||
CON_RESOLUCAO = ?
|
||||
WHERE CON_CODIGO_ID = ?
|
||||
`, [usuarioNome, equipeNome, etiquetasDesc, id]);
|
||||
`, [usuarioNome, equipeNome, etiquetasDesc, motivoId, resolucao || null, id]);
|
||||
|
||||
// CSAT
|
||||
const config = await db.query(alias,
|
||||
|
||||
Reference in New Issue
Block a user