fix: correzione ricerca cliente tramite LDAP. fix: vista dettaglio che interrogava l'endpoint sbagliato. PRovocato probabilmente ancora dall'unificazione delle note rapide con le note normali.
This commit is contained in:
1 parent
0b6907969d
commit
abb72d53c9
3 files changed
+136
-55
No files matched your search
@@ -4201,36 +4201,53 @@ const TicketDetailView = {
|
|||||||
customerSearchInput.addEventListener('input', () => {
|
customerSearchInput.addEventListener('input', () => {
|
||||||
clearTimeout(customerDebounce);
|
clearTimeout(customerDebounce);
|
||||||
const q = customerSearchInput.value.trim();
|
const q = customerSearchInput.value.trim();
|
||||||
if (customerUserIdInput) customerUserIdInput.value = '';
|
const origCustFirst = (this.originalValues['customer_first'] || '').trim();
|
||||||
if (customerIdInput) customerIdInput.value = '';
|
const origCustLast = (this.originalValues['customer_last'] || '').trim();
|
||||||
|
const origCustName = origCustFirst ? `${origCustFirst} ${origCustLast}`.trim() : (this.originalValues['customer_user_id'] || '').trim();
|
||||||
|
|
||||||
|
if (q !== origCustName) {
|
||||||
|
if (customerUserIdInput) customerUserIdInput.value = '';
|
||||||
|
if (customerIdInput) customerIdInput.value = '';
|
||||||
|
} else {
|
||||||
|
if (customerUserIdInput) customerUserIdInput.value = this.originalValues['customer_user_id'] || '';
|
||||||
|
if (customerIdInput) customerIdInput.value = this.originalValues['customer_id'] || '';
|
||||||
|
}
|
||||||
checkChanges();
|
checkChanges();
|
||||||
|
|
||||||
customerDebounce = setTimeout(async () => {
|
customerDebounce = setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
const customers = await App.api(`/api/customers/search?q=${encodeURIComponent(q)}`);
|
const users = await App.api(`/api/customer-users/search?q=${encodeURIComponent(q)}`);
|
||||||
if (!customers || customers.length === 0) {
|
if (!users || users.length === 0) {
|
||||||
customerSuggestionsDiv.innerHTML = '<div class="autocomplete-suggestion-item" style="color:var(--text-muted); cursor:default; padding: 6px 12px; font-size:0.78rem;">Nessun cliente trovato</div>';
|
customerSuggestionsDiv.innerHTML = '<div class="autocomplete-suggestion-item" style="color:var(--text-muted); cursor:default; padding: 6px 12px; font-size:0.78rem;">Nessun cliente trovato</div>';
|
||||||
customerSuggestionsDiv.style.display = 'block';
|
customerSuggestionsDiv.style.display = 'block';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
customerSuggestionsDiv.innerHTML = customers.map(c => `
|
customerSuggestionsDiv.innerHTML = users.map(u => {
|
||||||
<div class="autocomplete-suggestion-item" data-user-id="${App.escapeHtml(c.customer_user_id)}" data-customer-id="${App.escapeHtml(c.customer_id || c.customer_user_id)}" data-name="${App.escapeHtml(c.first_name ? `${c.first_name} ${c.last_name}` : c.customer_user_id)}" style="padding: 6px 12px; font-size: 0.78rem; cursor: pointer;">
|
const fullName = (u.first_name || u.last_name) ? `${u.first_name || ''} ${u.last_name || ''}`.trim() : (u.login || '');
|
||||||
<strong>${App.escapeHtml(c.first_name ? `${c.first_name} ${c.last_name}` : c.customer_user_id)}</strong>
|
return `
|
||||||
${c.email ? `<span style="color:var(--text-muted); margin-left:6px;">(${App.escapeHtml(c.email)})</span>` : ''}
|
<div class="autocomplete-suggestion-item" data-login="${App.escapeHtml(u.login || '')}" data-customer-id="${App.escapeHtml(u.customer_id || '')}" data-name="${App.escapeHtml(fullName)}" style="padding: 6px 12px; font-size: 0.78rem; cursor: pointer;">
|
||||||
</div>
|
<strong>${App.escapeHtml(fullName)}</strong>
|
||||||
`).join('');
|
<span style="color:var(--text-muted); margin-left:6px; font-size:0.75rem;">(Login: ${App.escapeHtml(u.login || '')}${u.customer_id ? ` | ${App.escapeHtml(u.customer_id)}` : ''})</span>
|
||||||
|
${u.email ? `<div style="color:var(--text-secondary); font-size:0.73rem;">${App.escapeHtml(u.email)}</div>` : ''}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
customerSuggestionsDiv.style.display = 'block';
|
customerSuggestionsDiv.style.display = 'block';
|
||||||
|
|
||||||
|
const selectCustomerItem = (item) => {
|
||||||
|
customerSearchInput.value = item.dataset.name;
|
||||||
|
if (customerUserIdInput) customerUserIdInput.value = item.dataset.login;
|
||||||
|
if (customerIdInput) customerIdInput.value = item.dataset.customerId;
|
||||||
|
customerSuggestionsDiv.style.display = 'none';
|
||||||
|
checkChanges();
|
||||||
|
};
|
||||||
|
|
||||||
customerSuggestionsDiv.querySelectorAll('.autocomplete-suggestion-item').forEach(item => {
|
customerSuggestionsDiv.querySelectorAll('.autocomplete-suggestion-item').forEach(item => {
|
||||||
if (item.dataset.userId) {
|
if (item.dataset.login) {
|
||||||
item.addEventListener('mousedown', (e) => {
|
item.addEventListener('mousedown', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
customerSearchInput.value = item.dataset.name;
|
selectCustomerItem(item);
|
||||||
if (customerUserIdInput) customerUserIdInput.value = item.dataset.userId;
|
|
||||||
if (customerIdInput) customerIdInput.value = item.dataset.customerId;
|
|
||||||
customerSuggestionsDiv.style.display = 'none';
|
|
||||||
checkChanges();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -4240,6 +4257,18 @@ const TicketDetailView = {
|
|||||||
}, 150);
|
}, 150);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (App.setupAutocompleteKeyboardNavigation) {
|
||||||
|
App.setupAutocompleteKeyboardNavigation(customerSearchInput, customerSuggestionsDiv, (item) => {
|
||||||
|
if (item && item.dataset.login) {
|
||||||
|
customerSearchInput.value = item.dataset.name;
|
||||||
|
if (customerUserIdInput) customerUserIdInput.value = item.dataset.login;
|
||||||
|
if (customerIdInput) customerIdInput.value = item.dataset.customerId;
|
||||||
|
customerSuggestionsDiv.style.display = 'none';
|
||||||
|
checkChanges();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
customerSearchInput.addEventListener('focus', () => {
|
customerSearchInput.addEventListener('focus', () => {
|
||||||
customerSearchInput.dispatchEvent(new Event('input'));
|
customerSearchInput.dispatchEvent(new Event('input'));
|
||||||
});
|
});
|
||||||
@@ -4270,9 +4299,18 @@ const TicketDetailView = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const customerSearch = customerSearchInput ? customerSearchInput.value.trim() : '';
|
const customerSearch = customerSearchInput ? customerSearchInput.value.trim() : '';
|
||||||
if (customerSearch && customerUserIdInput && !customerUserIdInput.value) {
|
const origCustFirst = (this.originalValues['customer_first'] || '').trim();
|
||||||
customerUserIdInput.value = customerSearch;
|
const origCustLast = (this.originalValues['customer_last'] || '').trim();
|
||||||
if (customerIdInput) customerIdInput.value = customerSearch;
|
const origCustName = origCustFirst ? `${origCustFirst} ${origCustLast}`.trim() : (this.originalValues['customer_user_id'] || '').trim();
|
||||||
|
|
||||||
|
if (customerSearchInput) {
|
||||||
|
if (!customerSearch) {
|
||||||
|
if (customerUserIdInput) customerUserIdInput.value = '';
|
||||||
|
if (customerIdInput) customerIdInput.value = '';
|
||||||
|
} else if (customerSearch === origCustName) {
|
||||||
|
if (customerUserIdInput) customerUserIdInput.value = this.originalValues['customer_user_id'] || '';
|
||||||
|
if (customerIdInput) customerIdInput.value = this.originalValues['customer_id'] || '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check metadata updates
|
// Check metadata updates
|
||||||
|
|||||||
@@ -296,6 +296,12 @@ router.get('/customer-companies/search', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Alias /api/customers/search -> /api/customer-users/search for backwards compatibility
|
||||||
|
router.get('/customers/search', (req, res, next) => {
|
||||||
|
req.url = '/customer-users/search' + (req.url.includes('?') ? req.url.slice(req.url.indexOf('?')) : '');
|
||||||
|
router.handle(req, res, next);
|
||||||
|
});
|
||||||
|
|
||||||
// GET /api/customer-users/search — Search customer users (supports LDAP via REST, fallbacks to DB)
|
// GET /api/customer-users/search — Search customer users (supports LDAP via REST, fallbacks to DB)
|
||||||
router.get('/customer-users/search', async (req, res) => {
|
router.get('/customer-users/search', async (req, res) => {
|
||||||
const { q = '', customer_company_id } = req.query;
|
const { q = '', customer_company_id } = req.query;
|
||||||
|
|||||||
+73
-36
@@ -387,7 +387,11 @@ router.get('/:id', async (req, res, next) => {
|
|||||||
// 1. Try to read from local SQLite cache in internal.db
|
// 1. Try to read from local SQLite cache in internal.db
|
||||||
let cachedUser = null;
|
let cachedUser = null;
|
||||||
try {
|
try {
|
||||||
cachedUser = db.prepare('SELECT email, first_name, last_name, phone FROM customer_user_cache WHERE login = ?').get(ticket.customer_user_id);
|
cachedUser = db.prepare(`
|
||||||
|
SELECT login, email, first_name, last_name, phone
|
||||||
|
FROM customer_user_cache
|
||||||
|
WHERE login = ? OR (first_name || ' ' || last_name) = ? COLLATE NOCASE
|
||||||
|
`).get(ticket.customer_user_id, ticket.customer_user_id);
|
||||||
} catch (dbErr) {
|
} catch (dbErr) {
|
||||||
console.warn('[LDAP Cache Detail] SQLite cache read failed:', dbErr.message);
|
console.warn('[LDAP Cache Detail] SQLite cache read failed:', dbErr.message);
|
||||||
}
|
}
|
||||||
@@ -398,37 +402,50 @@ router.get('/:id', async (req, res, next) => {
|
|||||||
ticket.customer_email = cachedUser.email || '';
|
ticket.customer_email = cachedUser.email || '';
|
||||||
ticket.customer_phone = cachedUser.phone || '';
|
ticket.customer_phone = cachedUser.phone || '';
|
||||||
} else {
|
} else {
|
||||||
// 2. Fallback to live API fetch
|
// 2. Fallback to live API fetch (only if login doesn't contain spaces)
|
||||||
const getResult = await otrsRequest('POST', '/CustomerUserGet', { UserLogin: ticket.customer_user_id });
|
let targetLogin = ticket.customer_user_id;
|
||||||
const getData = (getResult && getResult.Data) || getResult;
|
if (targetLogin.includes(' ')) {
|
||||||
if (getData && getData.CustomerUser) {
|
|
||||||
const u = getData.CustomerUser;
|
|
||||||
ticket.customer_first = u.UserFirstname || '';
|
|
||||||
ticket.customer_last = u.UserLastname || '';
|
|
||||||
ticket.customer_email = u.UserEmail || '';
|
|
||||||
ticket.customer_phone = u.UserPhone || '';
|
|
||||||
|
|
||||||
// 3. Store in SQLite cache for subsequent detail queries
|
|
||||||
try {
|
try {
|
||||||
db.prepare(`
|
const sRes = await otrsRequest('POST', '/CustomerUserSearch', { Search: `*${targetLogin}*`, Valid: 1 });
|
||||||
INSERT INTO customer_user_cache (login, email, first_name, last_name, customer_id, phone)
|
const sData = (sRes && sRes.Data) || sRes;
|
||||||
VALUES (?, ?, ?, ?, ?, ?)
|
if (sData && sData.CustomerUserID) {
|
||||||
ON CONFLICT(login) DO UPDATE SET
|
targetLogin = Array.isArray(sData.CustomerUserID) ? sData.CustomerUserID[0] : sData.CustomerUserID;
|
||||||
email=excluded.email,
|
}
|
||||||
first_name=excluded.first_name,
|
} catch (_) {}
|
||||||
last_name=excluded.last_name,
|
}
|
||||||
customer_id=excluded.customer_id,
|
|
||||||
phone=excluded.phone
|
if (targetLogin && !targetLogin.includes(' ')) {
|
||||||
`).run(
|
const getResult = await otrsRequest('POST', '/CustomerUserGet', { UserLogin: targetLogin });
|
||||||
u.UserLogin,
|
const getData = (getResult && getResult.Data) || getResult;
|
||||||
u.UserEmail || '',
|
if (getData && getData.CustomerUser) {
|
||||||
u.UserFirstname || '',
|
const u = getData.CustomerUser;
|
||||||
u.UserLastname || '',
|
ticket.customer_first = u.UserFirstname || '';
|
||||||
u.UserCustomerID || '',
|
ticket.customer_last = u.UserLastname || '';
|
||||||
u.UserPhone || ''
|
ticket.customer_email = u.UserEmail || '';
|
||||||
);
|
ticket.customer_phone = u.UserPhone || '';
|
||||||
} catch (cacheErr) {
|
|
||||||
console.warn('[LDAP Cache Detail] Failed to write fetched user to SQLite cache:', cacheErr.message);
|
// 3. Store in SQLite cache for subsequent detail queries
|
||||||
|
try {
|
||||||
|
db.prepare(`
|
||||||
|
INSERT INTO customer_user_cache (login, email, first_name, last_name, customer_id, phone)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
|
ON CONFLICT(login) DO UPDATE SET
|
||||||
|
email=excluded.email,
|
||||||
|
first_name=excluded.first_name,
|
||||||
|
last_name=excluded.last_name,
|
||||||
|
customer_id=excluded.customer_id,
|
||||||
|
phone=excluded.phone
|
||||||
|
`).run(
|
||||||
|
u.UserLogin,
|
||||||
|
u.UserEmail || '',
|
||||||
|
u.UserFirstname || '',
|
||||||
|
u.UserLastname || '',
|
||||||
|
u.UserCustomerID || '',
|
||||||
|
u.UserPhone || ''
|
||||||
|
);
|
||||||
|
} catch (cacheErr) {
|
||||||
|
console.warn('[LDAP Cache Detail] Failed to write fetched user to SQLite cache:', cacheErr.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -940,9 +957,19 @@ router.patch('/:id', async (req, res) => {
|
|||||||
if (updates.responsible_user_id !== undefined) ticketFields.ResponsibleID = updates.responsible_user_id;
|
if (updates.responsible_user_id !== undefined) ticketFields.ResponsibleID = updates.responsible_user_id;
|
||||||
if (updates.type_id !== undefined) ticketFields.TypeID = updates.type_id;
|
if (updates.type_id !== undefined) ticketFields.TypeID = updates.type_id;
|
||||||
if (updates.title !== undefined) ticketFields.Title = updates.title;
|
if (updates.title !== undefined) ticketFields.Title = updates.title;
|
||||||
if (updates.ticket_lock_id !== undefined) ticketFields.LockID = updates.ticket_lock_id;
|
if (updates.customer_user_id !== undefined && updates.customer_user_id !== null && updates.customer_user_id !== '') {
|
||||||
|
try {
|
||||||
|
const match = db.prepare(`SELECT login, customer_id FROM customer_user_cache WHERE login = ? OR (first_name || ' ' || last_name) = ? COLLATE NOCASE`).get(updates.customer_user_id, updates.customer_user_id);
|
||||||
|
if (match) {
|
||||||
|
updates.customer_user_id = match.login;
|
||||||
|
if (!updates.customer_id && match.customer_id) {
|
||||||
|
updates.customer_id = match.customer_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
ticketFields.CustomerUser = updates.customer_user_id;
|
||||||
|
}
|
||||||
if (updates.customer_id !== undefined && updates.customer_id !== null && updates.customer_id !== '') ticketFields.CustomerID = updates.customer_id;
|
if (updates.customer_id !== undefined && updates.customer_id !== null && updates.customer_id !== '') ticketFields.CustomerID = updates.customer_id;
|
||||||
if (updates.customer_user_id !== undefined && updates.customer_user_id !== null && updates.customer_user_id !== '') ticketFields.CustomerUser = updates.customer_user_id;
|
|
||||||
|
|
||||||
// If ticketFields is empty but we have a note or timeUnit to log, supply a dummy field (StateID) so Ticket is not empty
|
// If ticketFields is empty but we have a note or timeUnit to log, supply a dummy field (StateID) so Ticket is not empty
|
||||||
if (Object.keys(ticketFields).length === 0 && (hasNote || (!isNaN(timeUnit) && timeUnit > 0))) {
|
if (Object.keys(ticketFields).length === 0 && (hasNote || (!isNaN(timeUnit) && timeUnit > 0))) {
|
||||||
@@ -1673,9 +1700,19 @@ router.patch('/batch/update', async (req, res) => {
|
|||||||
if (updates.queue_id !== undefined) ticketFields.QueueID = updates.queue_id;
|
if (updates.queue_id !== undefined) ticketFields.QueueID = updates.queue_id;
|
||||||
if (updates.type_id !== undefined) ticketFields.TypeID = updates.type_id;
|
if (updates.type_id !== undefined) ticketFields.TypeID = updates.type_id;
|
||||||
if (updates.user_id !== undefined) ticketFields.OwnerID = updates.user_id;
|
if (updates.user_id !== undefined) ticketFields.OwnerID = updates.user_id;
|
||||||
if (updates.responsible_user_id !== undefined) ticketFields.ResponsibleID = updates.responsible_user_id;
|
if (updates.customer_user_id !== undefined && updates.customer_user_id !== null && updates.customer_user_id !== '') {
|
||||||
if (updates.customer_id !== undefined) ticketFields.CustomerID = updates.customer_id;
|
try {
|
||||||
if (updates.customer_user_id !== undefined) ticketFields.CustomerUser = updates.customer_user_id;
|
const match = db.prepare(`SELECT login, customer_id FROM customer_user_cache WHERE login = ? OR (first_name || ' ' || last_name) = ? COLLATE NOCASE`).get(updates.customer_user_id, updates.customer_user_id);
|
||||||
|
if (match) {
|
||||||
|
updates.customer_user_id = match.login;
|
||||||
|
if (!updates.customer_id && match.customer_id) {
|
||||||
|
updates.customer_id = match.customer_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
ticketFields.CustomerUser = updates.customer_user_id;
|
||||||
|
}
|
||||||
|
if (updates.customer_id !== undefined && updates.customer_id !== null && updates.customer_id !== '') ticketFields.CustomerID = updates.customer_id;
|
||||||
|
|
||||||
// Auto sblocco check
|
// Auto sblocco check
|
||||||
if (updates.ticket_state_id) {
|
if (updates.ticket_state_id) {
|
||||||
|
|||||||
Reference in new issue
Block a user