fix: ora le ricerche asincrone non si sovrappongono più alle attività. feat: aggiunta funzione Storico Consuntivazione

This commit is contained in:
VVER440 committed 2026-08-21 00:07:08 +02:00
1 parent ba23635c7d
commit 3650bf5790
9 files changed
+737 -33

No files matched your search

+96 -4
View File
@@ -754,7 +754,7 @@ router.patch('/:id', async (req, res) => {
let current;
try {
const currentResult = await pool.query(
`SELECT ticket_state_id, ticket_priority_id, queue_id, user_id, responsible_user_id, type_id, title, ticket_lock_id, customer_id, customer_user_id
`SELECT tn, ticket_state_id, ticket_priority_id, queue_id, user_id, responsible_user_id, type_id, title, ticket_lock_id, customer_id, customer_user_id
FROM ticket WHERE id = $1`,
[id]
);
@@ -832,7 +832,7 @@ router.patch('/:id', async (req, res) => {
agente_id: operatorId,
agente_nome,
titolo_azione: isClosing ? 'Chiusura Ticket' : 'Modifica Rapida Ticket',
azione: { ticket_id: id, ...updates },
azione: { ticket_id: id, tn: current.tn, ...updates },
esito: 'successo',
});
});
@@ -1048,7 +1048,7 @@ router.patch('/:id', async (req, res) => {
agente_id: operatorId,
agente_nome,
titolo_azione: isClosingDB ? 'Chiusura Ticket' : 'Modifica Rapida Ticket',
azione: { ticket_id: id, ...updates },
azione: { ticket_id: id, tn: current.tn, ...updates },
esito: 'successo',
});
});
@@ -1392,7 +1392,7 @@ router.post('/:id/articles', async (req, res) => {
agente_id: operatorId,
agente_nome,
titolo_azione: 'Aggiunta Nota',
azione: { ticket_id: id, subject, time_unit, has_attachments: !!(attachments && attachments.length) },
azione: { ticket_id: id, tn: t.tn, subject, time_unit, has_attachments: !!(attachments && attachments.length) },
esito: 'successo',
});
});
@@ -1874,6 +1874,98 @@ router.post('/merge', async (req, res) => {
}
});
// ============================================================
// GET /api/tickets/time-accounting/summary — Summary of notes/emails and time per agent per day
// ============================================================
router.get('/time-accounting/summary', async (req, res) => {
try {
const operatorId = parseInt(req.headers['x-agent-id'], 10) || 1;
const agentId = parseInt(req.query.agent_id, 10) || operatorId;
const dateStr = req.query.date || new Date().toISOString().slice(0, 10);
const targetDate = new Date(`${dateStr}T00:00:00Z`);
const startDateObj = new Date(targetDate);
startDateObj.setDate(startDateObj.getDate() - 7);
const endDateObj = new Date(targetDate);
endDateObj.setDate(endDateObj.getDate() + 7);
const startTime = `${startDateObj.toISOString().slice(0, 10)} 00:00:00`;
const endTime = `${endDateObj.toISOString().slice(0, 10)} 23:59:59`;
const result = await pool.query(
`SELECT
a.id AS article_id,
a.ticket_id,
t.tn AS ticket_tn,
t.title AS ticket_title,
ast.name AS sender_type,
cc.name AS channel,
adm.a_subject,
adm.a_body,
adm.a_from,
adm.a_to,
a.create_time,
COALESCE(ta.time_unit, 0) AS time_unit
FROM article a
JOIN ticket t ON a.ticket_id = t.id
JOIN article_sender_type ast ON a.article_sender_type_id = ast.id
LEFT JOIN communication_channel cc ON a.communication_channel_id = cc.id
LEFT JOIN article_data_mime adm ON a.id = adm.article_id
LEFT JOIN (
SELECT article_id, SUM(time_unit) AS time_unit
FROM time_accounting
GROUP BY article_id
) ta ON a.id = ta.article_id
WHERE a.create_by = $1
AND a.create_time >= $2
AND a.create_time <= $3
ORDER BY a.create_time DESC, a.id DESC`,
[agentId, startTime, endTime]
);
const dailyTotals = {};
const dayArticles = [];
let totalMinutes = 0;
result.rows.forEach(r => {
let isoDate = '';
if (r.create_time instanceof Date) {
const y = r.create_time.getUTCFullYear();
const m = String(r.create_time.getUTCMonth() + 1).padStart(2, '0');
const d = String(r.create_time.getUTCDate()).padStart(2, '0');
isoDate = `${y}-${m}-${d}`;
} else {
isoDate = String(r.create_time || '').slice(0, 10);
}
const timeVal = parseFloat(r.time_unit || 0);
dailyTotals[isoDate] = (dailyTotals[isoDate] || 0) + timeVal;
if (isoDate === dateStr) {
dayArticles.push(r);
totalMinutes += timeVal;
}
});
const targetMinutes = parseInt(process.env.DAILY_TARGET_TIME, 10) || 480;
const workDaysRaw = process.env.WORK_DAYS || '1,2,3,4,5';
const workDays = workDaysRaw.split(',').map(x => parseInt(x.trim(), 10)).filter(x => !isNaN(x));
res.json({
agent_id: agentId,
date: dateStr,
total_minutes: totalMinutes,
target_minutes: targetMinutes,
work_days: workDays,
articles: dayArticles,
daily_totals: dailyTotals,
});
} catch (err) {
console.error('Error fetching time accounting summary:', err);
res.status(500).json({ error: err.message });
}
});
// ============================================================
// PUT /api/articles/:articleId/time — Update time accounted for an article
// ============================================================