fix: tentativo di fix per il riconoscimento del ticket ma otrs è stronzo-merda. feat: nella pagina di anteprima del ticket, aggiunto pulsante per aprire in una scheda interna.
This commit is contained in:
@@ -433,10 +433,8 @@ const EmailCompose = (() => {
|
||||
} else {
|
||||
const tn = options.ticketTn || '';
|
||||
const title = options.ticketTitle || '';
|
||||
subjectEl.value = tn ? `Re: [Ticket#${tn}] ${title}` : title;
|
||||
subjectEl.value = tn ? `[Ticket#${tn}] Re: ${title}` : title;
|
||||
}
|
||||
|
||||
// Signature and groups select
|
||||
const sigSelect = document.getElementById('ec-signature-select');
|
||||
const groupsSelect = document.getElementById('ec-groups-select');
|
||||
const agentId = App.currentAgentId || 0;
|
||||
|
||||
@@ -161,6 +161,7 @@ const TicketDetailView = {
|
||||
<h2 class="ticket-detail-title" style="display:inline-flex; align-items:center; gap:6px; flex-wrap:wrap;">
|
||||
<span class="copy-ticket-btn" data-tn="${App.escapeHtml(ticket.title || '')}" style="cursor: pointer; font-size: 0.85rem;" title="Copia titolo ticket">📋</span>
|
||||
${App.escapeHtml(ticket.title || '(senza titolo)')}
|
||||
<button class="open-tab-btn" data-id="${ticket.id}" data-tn="${ticket.tn}" data-title="${App.escapeHtml(ticket.title || '(senza titolo)')}" onclick="App.openTab(${ticket.id}, '${ticket.tn}', this.dataset.title); event.stopPropagation();" title="Apri in scheda" style="display:inline-flex; align-items:center; justify-content:center; width:16px; height:16px; background:var(--accent-primary); color:#fff; border:none; border-radius:50%; font-size:10px; font-weight:800; font-family:sans-serif; margin-left:6px; cursor:pointer; line-height:16px;">+</button>
|
||||
</h2>
|
||||
<div class="ticket-meta-badges">
|
||||
<span class="badge badge-state" data-state-type="${(ticket.state_type || '').toLowerCase()}">${ticket.state_name}</span>
|
||||
@@ -566,7 +567,7 @@ const TicketDetailView = {
|
||||
this.updateNoteAttachmentList();
|
||||
}
|
||||
|
||||
this.bindEvents(ticket, articles, container, groupsData);
|
||||
this.bindEvents(ticket, articles, container, groupsData, attachments);
|
||||
|
||||
} catch (err) {
|
||||
container.innerHTML = `
|
||||
@@ -580,7 +581,7 @@ const TicketDetailView = {
|
||||
}
|
||||
},
|
||||
|
||||
bindEvents(ticket, articles, container, groupsData) {
|
||||
bindEvents(ticket, articles, container, groupsData, attachments) {
|
||||
// Quick-edit change detection
|
||||
const fields = document.querySelectorAll('.quick-edit-select:not(#qe-queue-search), #qe-queue, #qe-customer-user-id, #qe-customer-id');
|
||||
const saveBtn = document.getElementById('qe-save');
|
||||
|
||||
+4
-3
@@ -226,7 +226,7 @@ router.post('/send', async (req, res) => {
|
||||
if (!ticketResult.rows.length) return res.status(404).json({ error: 'Ticket non trovato' });
|
||||
|
||||
const { tn, title } = ticketResult.rows[0];
|
||||
const subject = customSubject || `Re: [Ticket#${tn}] ${title}`;
|
||||
const subject = customSubject || `[Ticket#${tn}] Re: ${title}`;
|
||||
|
||||
// 2. Build BCC list (include OTRS system mailbox if keepHelpdeskCopy is true)
|
||||
const bccList = [...bcc];
|
||||
@@ -263,7 +263,8 @@ router.post('/send', async (req, res) => {
|
||||
const messageId = `<${Date.now()}.${Math.random().toString(36).substring(2)}@pharmaidea.com>`;
|
||||
|
||||
// 3. Send via configured mailer (Graph API or SMTP)
|
||||
await sendMail({ to, cc, bcc: bccList, subject, bodyHtml: processedBodyHtml, attachments, inlineImages: finalInlineImages, inReplyTo, references, messageId });
|
||||
const mailResult = await sendMail({ to, cc, bcc: bccList, subject, bodyHtml: processedBodyHtml, attachments, inlineImages: finalInlineImages, inReplyTo, references, messageId });
|
||||
const finalMessageId = (mailResult && mailResult.internetMessageId) || messageId;
|
||||
|
||||
// 4. Log article in OTRS ticket via DB as a standard Email article
|
||||
try {
|
||||
@@ -308,7 +309,7 @@ router.post('/send', async (req, res) => {
|
||||
await pool.query(`
|
||||
INSERT INTO article_data_mime (article_id, a_from, a_to, a_cc, a_bcc, a_subject, a_body, a_content_type, a_message_id, incoming_time, create_time, create_by, change_time, change_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, 'text/html; charset=utf-8', $8, $9, $11, $10, $11, $10)`,
|
||||
[articleId, aFrom, toList, cc.join(', '), bccList.join(', '), subject, processedBodyHtml, messageId, now, agentId || 1, localNow]
|
||||
[articleId, aFrom, toList, cc.join(', '), bccList.join(', '), subject, processedBodyHtml, finalMessageId, now, agentId || 1, localNow]
|
||||
);
|
||||
|
||||
// Helper to strip HTML tags
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require('dotenv').config();
|
||||
require('dotenv').config({ path: require('path').resolve(__dirname, '.env') });
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const path = require('path');
|
||||
|
||||
+26
-3
@@ -60,7 +60,7 @@ async function getAccessToken() {
|
||||
* @param {Array} [options.inlineImages] - [{ cid, content (base64), contentType }]
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function sendMail({ to, cc = [], bcc = [], subject, bodyHtml, attachments = [], inlineImages = [], inReplyTo, references }) {
|
||||
async function sendMail({ to, cc = [], bcc = [], subject, bodyHtml, attachments = [], inlineImages = [], inReplyTo, references, messageId }) {
|
||||
const sender = process.env.AZURE_MAIL_SENDER;
|
||||
if (!sender) throw new Error('AZURE_MAIL_SENDER non configurato nel .env');
|
||||
|
||||
@@ -115,7 +115,7 @@ async function sendMail({ to, cc = [], bcc = [], subject, bodyHtml, attachments
|
||||
attachments: allAttachments,
|
||||
internetMessageHeaders: headers.length ? headers : undefined,
|
||||
},
|
||||
saveToSentItems: false,
|
||||
saveToSentItems: true,
|
||||
};
|
||||
|
||||
const url = `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(sender)}/sendMail`;
|
||||
@@ -130,7 +130,30 @@ async function sendMail({ to, cc = [], bcc = [], subject, bodyHtml, attachments
|
||||
});
|
||||
|
||||
if (res.status === 202) {
|
||||
return; // Successo (Graph API risponde con 202 No Content)
|
||||
let actualMessageId = messageId;
|
||||
try {
|
||||
// Wait 1.5 seconds for Exchange to process and place it in Sent Items
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
|
||||
const searchUrl = `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(sender)}/mailFolders/sentItems/messages?$filter=subject eq '${subject.replace(/'/g, "''")}'&$top=1&$select=internetMessageId`;
|
||||
const searchRes = await fetch(searchUrl, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (searchRes.ok) {
|
||||
const searchData = await searchRes.json();
|
||||
if (searchData.value && searchData.value.length > 0) {
|
||||
actualMessageId = searchData.value[0].internetMessageId;
|
||||
}
|
||||
}
|
||||
} catch (searchErr) {
|
||||
console.warn('[Graph Mailer] Failed to retrieve actual InternetMessageId from Sent Items:', searchErr.message);
|
||||
}
|
||||
|
||||
return { internetMessageId: actualMessageId };
|
||||
}
|
||||
|
||||
const errText = await res.text();
|
||||
|
||||
+3
-1
@@ -29,7 +29,7 @@ function getTransporter() {
|
||||
* Invia una email tramite SMTP (nodemailer).
|
||||
* Stessa interfaccia di graphMailer.sendMail.
|
||||
*/
|
||||
async function sendMail({ to, cc = [], bcc = [], subject, bodyHtml, attachments = [], inlineImages = [], inReplyTo, references }) {
|
||||
async function sendMail({ to, cc = [], bcc = [], subject, bodyHtml, attachments = [], inlineImages = [], inReplyTo, references, messageId }) {
|
||||
const transporter = getTransporter();
|
||||
|
||||
const mailOptions = {
|
||||
@@ -41,6 +41,7 @@ async function sendMail({ to, cc = [], bcc = [], subject, bodyHtml, attachments
|
||||
html: bodyHtml,
|
||||
inReplyTo,
|
||||
references,
|
||||
messageId,
|
||||
attachments: [
|
||||
...attachments.map(a => ({
|
||||
filename: a.filename,
|
||||
@@ -57,6 +58,7 @@ async function sendMail({ to, cc = [], bcc = [], subject, bodyHtml, attachments
|
||||
};
|
||||
|
||||
await transporter.sendMail(mailOptions);
|
||||
return { internetMessageId: messageId };
|
||||
}
|
||||
|
||||
module.exports = { sendMail };
|
||||
|
||||
Reference in New Issue
Block a user