fix(newwhats): papel do número no /wa-inbox p/ usuário sem conta no motor

Os papéis (sec_numbers) são resolvidos no motor pela conta (x-nw-email). Um
usuário que só existe no scoreodonto (ex.: recepcionista) não tem conta no motor,
então buscar os papéis com o e-mail DELE retorna "conta não encontrada" → todos os
números liberados apareciam como "Sem papel definido". Agora o papel de cada sessão
liberada é buscado com o e-mail do DONO da sessão (cache por e-mail). Verificado:
recep passa a ver o número como role=clinic ("Clínica / Recepção · Rui Cesar").

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Builder
2026-07-06 17:24:16 +02:00
parent 26f39a60f3
commit 4dea9ab28a
+21 -10
View File
@@ -301,29 +301,39 @@ export function createAccountsRoute(pool) {
};
try {
// Papéis: sec_numbers são globais no motor — um fetch, indexado por instance_id.
const numbers = await motorGet('/secretaria/numbers', email);
const roleOf = new Map(numbers.map((n) => [n.instance_id, n]));
const meta = (instanceId, fallbackName) => {
const n = roleOf.get(instanceId) || {};
// Papéis (sec_numbers) são resolvidos no motor pela CONTA (x-nw-email). Um
// usuário sem conta no motor (ex.: recepcionista que só existe no scoreodonto)
// não tem números próprios — então o papel de cada número LIBERADO precisa ser
// buscado com o e-mail do DONO da sessão, não do usuário logado. Cache por e-mail.
const numbersCache = new Map();
const numbersFor = async (asEmail) => {
if (!numbersCache.has(asEmail)) {
const nums = await motorGet('/secretaria/numbers', asEmail);
numbersCache.set(asEmail, new Map(nums.map((n) => [n.instance_id, n])));
}
return numbersCache.get(asEmail);
};
const metaFrom = (roleMap, instanceId, fallbackName) => {
const n = roleMap.get(instanceId) || {};
return { role: n.role || null, label: n.label || fallbackName || null, area: n.area || null, notes: n.notes || null };
};
const out = [];
const seen = new Set();
const push = (s, extra) => {
const pushWith = (s, roleMap, extra) => {
if (!s || seen.has(s.id)) return;
seen.add(s.id);
out.push({
instanceId: s.id, phone: s.phone || null, name: s.name || null,
status: s.status || null, avatar: s.avatar || null,
...meta(s.id, s.name), ...extra,
...metaFrom(roleMap, s.id, s.name), ...extra,
});
};
// 1) Sessões PRÓPRIAS (conta do próprio usuário no motor).
// 1) Sessões PRÓPRIAS (conta do próprio usuário no motor) — papel da própria conta.
const ownRoles = await numbersFor(email);
for (const s of await motorGet('/sessions', email)) {
push(s, { clinicaId: null, ownerEmail: email, ownerNome: null, own: true, canInbox: true });
pushWith(s, ownRoles, { clinicaId: null, ownerEmail: email, ownerNome: null, own: true, canInbox: true });
}
// 2) Sessões LIBERADAS por outros donos (can_inbox por sessão).
@@ -343,8 +353,9 @@ export function createAccountsRoute(pool) {
const { rows } = await pool.query('SELECT nome FROM usuarios WHERE id = $1', [owner.ownerId]);
ownerNome = rows[0]?.nome || null;
} catch { /* opcional */ }
const ownerRoles = await numbersFor(owner.email); // papéis dos números vêm da conta do DONO
for (const s of await motorGet('/sessions', owner.email)) {
if (allowed.has(s.id)) push(s, { clinicaId, ownerEmail: owner.email, ownerNome, own: false, canInbox: true });
if (allowed.has(s.id)) pushWith(s, ownerRoles, { clinicaId, ownerEmail: owner.email, ownerNome, own: false, canInbox: true });
}
}