feat: potenziata visualizzazione storico consuntivazione. style: disabilitata apertura massiva. feat: migliorata modifica pdf e scaricamento di tutti gli allegati tramite shortcut.
This commit is contained in:
1 parent
b1754c3a67
commit
c7b18ac959
10 files changed
+1545
-191
No files matched your search
+95
-39
@@ -2151,50 +2151,98 @@ router.post('/merge', async (req, res) => {
|
||||
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 agentIdsRaw = req.query.agent_ids || req.query.agent_id || String(operatorId);
|
||||
const agentIds = agentIdsRaw.split(',').map(x => parseInt(x.trim(), 10)).filter(x => !isNaN(x));
|
||||
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() - 14);
|
||||
const endDateObj = new Date(targetDate);
|
||||
endDateObj.setDate(endDateObj.getDate() + 14);
|
||||
const rangeParam = req.query.range || '35';
|
||||
const isAll = rangeParam === 'all';
|
||||
let result;
|
||||
if (isAll) {
|
||||
result = await pool.query(
|
||||
`SELECT
|
||||
a.id AS article_id,
|
||||
a.ticket_id,
|
||||
a.create_by AS agent_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 = ANY($1::int[])
|
||||
ORDER BY a.create_time DESC, a.id DESC`,
|
||||
[agentIds]
|
||||
);
|
||||
} else {
|
||||
const targetDate = new Date(`${dateStr}T00:00:00Z`);
|
||||
let startDateObj, endDateObj;
|
||||
|
||||
const startTime = `${startDateObj.toISOString().slice(0, 10)} 00:00:00`;
|
||||
const endTime = `${endDateObj.toISOString().slice(0, 10)} 23:59:59`;
|
||||
if (rangeParam === 'month') {
|
||||
const y = targetDate.getUTCFullYear();
|
||||
const m = targetDate.getUTCMonth();
|
||||
startDateObj = new Date(Date.UTC(y, m, 1));
|
||||
endDateObj = new Date(Date.UTC(y, m + 1, 0));
|
||||
} else {
|
||||
const daysBack = parseInt(rangeParam, 10) || 35;
|
||||
startDateObj = new Date(targetDate);
|
||||
startDateObj.setDate(startDateObj.getDate() - daysBack);
|
||||
endDateObj = new Date(targetDate);
|
||||
endDateObj.setDate(endDateObj.getDate() + 5);
|
||||
}
|
||||
|
||||
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 startTime = `${startDateObj.toISOString().slice(0, 10)} 00:00:00`;
|
||||
const endTime = `${endDateObj.toISOString().slice(0, 10)} 23:59:59`;
|
||||
|
||||
result = await pool.query(
|
||||
`SELECT
|
||||
a.id AS article_id,
|
||||
a.ticket_id,
|
||||
a.create_by AS agent_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 = ANY($1::int[])
|
||||
AND a.create_time >= $2
|
||||
AND a.create_time <= $3
|
||||
ORDER BY a.create_time DESC, a.id DESC`,
|
||||
[agentIds, startTime, endTime]
|
||||
);
|
||||
}
|
||||
|
||||
const dailyTotals = {};
|
||||
const dailyTotalsByAgent = {};
|
||||
const dayArticles = [];
|
||||
let totalMinutes = 0;
|
||||
|
||||
@@ -2209,9 +2257,15 @@ router.get('/time-accounting/summary', async (req, res) => {
|
||||
isoDate = String(r.create_time || '').slice(0, 10);
|
||||
}
|
||||
const timeVal = parseFloat(r.time_unit || 0);
|
||||
const agId = r.agent_id;
|
||||
|
||||
dailyTotals[isoDate] = (dailyTotals[isoDate] || 0) + timeVal;
|
||||
|
||||
if (!dailyTotalsByAgent[agId]) {
|
||||
dailyTotalsByAgent[agId] = {};
|
||||
}
|
||||
dailyTotalsByAgent[agId][isoDate] = (dailyTotalsByAgent[agId][isoDate] || 0) + timeVal;
|
||||
|
||||
if (isoDate === dateStr) {
|
||||
dayArticles.push(r);
|
||||
totalMinutes += timeVal;
|
||||
@@ -2223,13 +2277,15 @@ router.get('/time-accounting/summary', async (req, res) => {
|
||||
const workDays = workDaysRaw.split(',').map(x => parseInt(x.trim(), 10)).filter(x => !isNaN(x));
|
||||
|
||||
res.json({
|
||||
agent_id: agentId,
|
||||
agent_id: agentIds[0],
|
||||
agent_ids: agentIds,
|
||||
date: dateStr,
|
||||
total_minutes: totalMinutes,
|
||||
target_minutes: targetMinutes,
|
||||
work_days: workDays,
|
||||
articles: dayArticles,
|
||||
daily_totals: dailyTotals,
|
||||
daily_totals_by_agent: dailyTotalsByAgent,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Error fetching time accounting summary:', err);
|
||||
|
||||
Reference in new issue
Block a user