fix: note temporanee che non funzionavano. feat: le note possono essere inviate ad otrs senza numero di ticket. style: rivisti alcuni nomi e colore per invio di una nota temporanea in otrs.
This commit is contained in:
1 parent
b828d60bb2
commit
f21be0abcb
9 files changed
+216
-70
No files matched your search
+88
-14
@@ -135,13 +135,12 @@ router.post('/:id/submit', async (req, res) => {
|
||||
return res.status(404).json({ error: 'Nota temporanea non trovata' });
|
||||
}
|
||||
|
||||
if (!note.ticket_id && !note.ticket_tn) {
|
||||
return res.status(400).json({ error: 'Impossibile inviare a OTRS: nessun ticket (TN/ID) associato a questa nota' });
|
||||
}
|
||||
|
||||
const resolved = await resolveTicketInfo(note.ticket_id, note.ticket_tn);
|
||||
if (!resolved) {
|
||||
return res.status(400).json({ error: `Ticket non trovato nel sistema OTRS (TN: ${note.ticket_tn || ''}, ID: ${note.ticket_id || ''})` });
|
||||
let resolved = null;
|
||||
if (note.ticket_id || note.ticket_tn) {
|
||||
resolved = await resolveTicketInfo(note.ticket_id, note.ticket_tn);
|
||||
if (!resolved) {
|
||||
return res.status(400).json({ error: `Ticket non trovato nel sistema OTRS (TN: ${note.ticket_tn || ''}, ID: ${note.ticket_id || ''})` });
|
||||
}
|
||||
}
|
||||
|
||||
// Get agent login user
|
||||
@@ -156,6 +155,81 @@ router.post('/:id/submit', async (req, res) => {
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
// If no ticket is associated, create a new ticket from this note
|
||||
if (!resolved) {
|
||||
// Default queue (first queue or 1)
|
||||
let queueId = 1;
|
||||
const qRes = await pool.query('SELECT id FROM queue ORDER BY id ASC LIMIT 1');
|
||||
if (qRes.rows.length > 0) queueId = qRes.rows[0].id;
|
||||
|
||||
// Default state ('new' / 1)
|
||||
let stateId = 1;
|
||||
const sRes = await pool.query(`SELECT ts.id FROM ticket_state ts JOIN ticket_state_type tst ON ts.type_id = tst.id WHERE tst.name = 'new' LIMIT 1`);
|
||||
if (sRes.rows.length > 0) stateId = sRes.rows[0].id;
|
||||
|
||||
const ticketTitle = note.subject || 'Nuova Nota Temporanea';
|
||||
|
||||
// Generate TN
|
||||
const now = new Date();
|
||||
const systemId = process.env.OTRS_SYSTEM_ID || String(agentId);
|
||||
const datePrefix = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}${systemId}`;
|
||||
|
||||
const maxTicketResult = await pool.query(
|
||||
`SELECT tn FROM ticket WHERE tn LIKE $1`,
|
||||
[`${datePrefix}%`]
|
||||
);
|
||||
let maxCounter = 0;
|
||||
for (const row of maxTicketResult.rows) {
|
||||
const counterPart = row.tn.substring(datePrefix.length);
|
||||
const parsed = parseInt(counterPart, 10);
|
||||
if (!isNaN(parsed) && parsed > maxCounter) {
|
||||
maxCounter = parsed;
|
||||
}
|
||||
}
|
||||
const counterPadding = parseInt(process.env.OTRS_COUNTER_PADDING, 10) || 6;
|
||||
const newTn = `${datePrefix}${String(maxCounter + 1).padStart(counterPadding, '0')}`;
|
||||
|
||||
// Insert new ticket into DB
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
const nowTs = new Date().toISOString().replace('T', ' ').substring(0, 19);
|
||||
|
||||
const newTicketRes = await client.query(
|
||||
`INSERT INTO ticket (
|
||||
tn, title, queue_id, ticket_lock_id, type_id,
|
||||
user_id, responsible_user_id, ticket_priority_id, ticket_state_id,
|
||||
create_time, create_by, change_time, change_by
|
||||
) VALUES (
|
||||
$1, $2, $3, 1, 1,
|
||||
$4, $4, 3, $5,
|
||||
$6, $4, $6, $4
|
||||
) RETURNING id, tn`,
|
||||
[newTn, ticketTitle, queueId, agentId, stateId, nowTs]
|
||||
);
|
||||
|
||||
const newTicketId = newTicketRes.rows[0].id;
|
||||
resolved = { id: newTicketId, tn: newTn, title: ticketTitle };
|
||||
|
||||
// Insert NewTicket history
|
||||
const htRes = await client.query(`SELECT id FROM ticket_history_type WHERE name = 'NewTicket'`);
|
||||
const htId = htRes.rows.length > 0 ? htRes.rows[0].id : 1;
|
||||
await client.query(
|
||||
`INSERT INTO ticket_history (
|
||||
name, history_type_id, ticket_id, type_id, queue_id, owner_id, priority_id, state_id, create_time, create_by, change_time, change_by
|
||||
) VALUES ('%%', $1, $2, 1, $3, $4, 3, $5, $6, $4, $6, $4)`,
|
||||
[htId, newTicketId, queueId, agentId, stateId, nowTs]
|
||||
);
|
||||
|
||||
await client.query('COMMIT');
|
||||
} catch (createErr) {
|
||||
await client.query('ROLLBACK');
|
||||
throw createErr;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare Article for OTRS REST API
|
||||
const isInternal = note.note_type === 'note-internal';
|
||||
const isEmail = note.note_type === 'email-external';
|
||||
@@ -195,13 +269,13 @@ router.post('/:id/submit', async (req, res) => {
|
||||
}
|
||||
|
||||
// Log action
|
||||
logAttivita(
|
||||
agentId,
|
||||
userLogin,
|
||||
`Aggiunta nota a Ticket #${resolved.tn}`,
|
||||
{ ticket_id: resolved.id, ticket_tn: resolved.tn, subject: note.subject, time_units: note.time_units },
|
||||
'successo'
|
||||
);
|
||||
logAttivita({
|
||||
agente_id: agentId,
|
||||
agente_nome: userLogin,
|
||||
titolo_azione: `Aggiunta nota a Ticket #${resolved.tn}`,
|
||||
azione: { ticket_id: resolved.id, ticket_tn: resolved.tn, subject: note.subject, time_units: note.time_units },
|
||||
esito: 'successo'
|
||||
});
|
||||
|
||||
// Delete note from local SQLite DB upon successful OTRS upload
|
||||
db.prepare('DELETE FROM ticket_draft_notes WHERE id = ?').run(note.id);
|
||||
|
||||
+8
-7
@@ -482,7 +482,7 @@ router.post('/', async (req, res) => {
|
||||
|
||||
// Generate ticket number: get next counter value for today (daily reset)
|
||||
const now = new Date();
|
||||
const systemId = process.env.OTRS_SYSTEM_ID || '10';
|
||||
const systemId = process.env.OTRS_SYSTEM_ID || String(operatorId);
|
||||
const datePrefix = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}${systemId}`;
|
||||
|
||||
// 1. Fetch current maximum counter in ticket table for today
|
||||
@@ -748,8 +748,9 @@ router.patch('/:id', async (req, res) => {
|
||||
const updates = req.body; // { ticket_state_id, ticket_priority_id, queue_id, user_id, type_id, title, note }
|
||||
const operatorId = parseInt(req.headers['x-agent-id'], 10) || 1;
|
||||
const timeUnit = parseFloat(updates.time_unit);
|
||||
const noteSubjectText = typeof updates.note_subject === 'string' ? updates.note_subject.trim() : '';
|
||||
const noteText = typeof updates.note === 'string' ? updates.note.trim() : (typeof updates.note_text === 'string' ? updates.note_text.trim() : '');
|
||||
const hasNote = noteText.length > 0;
|
||||
const hasNote = noteText.length > 0 || noteSubjectText.length > 0;
|
||||
|
||||
// Fetch current ticket first (needed for both REST dummy fields and database fallback)
|
||||
let current;
|
||||
@@ -810,8 +811,8 @@ router.patch('/:id', async (req, res) => {
|
||||
Ticket: ticketFields
|
||||
};
|
||||
if (hasNote || (!isNaN(timeUnit) && timeUnit > 0)) {
|
||||
const noteSubject = hasNote ? noteText : 'Consuntivazione';
|
||||
const noteBody = hasNote ? noteText : 'Consuntivazione';
|
||||
const noteSubject = noteSubjectText || (hasNote ? noteText : 'Consuntivazione');
|
||||
const noteBody = noteText || noteSubjectText || 'Consuntivazione';
|
||||
const articleObj = {
|
||||
CommunicationChannel: 'Internal',
|
||||
SenderType: 'agent',
|
||||
@@ -993,8 +994,8 @@ router.patch('/:id', async (req, res) => {
|
||||
const articleId = articleResult.rows[0].id;
|
||||
const now = new Date();
|
||||
const contentPath = `${now.getFullYear()}/${String(now.getMonth() + 1).padStart(2, '0')}/${String(now.getDate()).padStart(2, '0')}`;
|
||||
const noteSubject = hasNote ? noteText : 'Consuntivazione';
|
||||
const noteBodyRaw = hasNote ? noteText : 'Consuntivazione';
|
||||
const noteSubject = noteSubjectText || (hasNote ? noteText : 'Consuntivazione');
|
||||
const noteBodyRaw = noteText || noteSubjectText || 'Consuntivazione';
|
||||
const safeBody = noteBodyRaw.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br/>');
|
||||
const htmlBody = `<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/></head><body style="font-family:Geneva,Helvetica,Arial,sans-serif; font-size: 12px;">${safeBody}</body></html>`;
|
||||
const binaryBody = Buffer.from(htmlBody, 'utf-8');
|
||||
@@ -2210,7 +2211,7 @@ router.post('/auto-time', async (req, res) => {
|
||||
|
||||
// Generate ticket number (daily reset)
|
||||
const now = new Date();
|
||||
const systemId = process.env.OTRS_SYSTEM_ID || '10';
|
||||
const systemId = process.env.OTRS_SYSTEM_ID || String(operatorId);
|
||||
const datePrefix = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}${systemId}`;
|
||||
|
||||
const maxTicketResult = await client.query(
|
||||
|
||||
Reference in new issue
Block a user