8353a9d897
O protético identifica o trabalho pelo nome do paciente (etiqueta, sem CPF — ele não atende
paciente). Quando dois trabalhos têm o mesmo nome+sobrenome, desambigua sem expor dado sensível:
- util pacienteLabel.ts (rotulosPaciente / homonimoAtivo): comparação normalizada
(case/acento/espaço-insensitive). Em colisão: 1) diferencia pela ORIGEM (dentista, senão
clínica) → "João Silva · Dr. Carlos"; 2) se mesma origem, nº sequencial estável por data.
- Bancada (ProteseView) e Painel do lab (LabHomeView/Próximas entregas): nome exibido já
desambiguado em toda a fila — vale para OS nascidas na clínica ou no lab.
- Modal "Dar entrada": campo único trocado por Nome + Sobrenome(s); aviso em tempo real quando
há homônimo ativo ("acrescente outro sobrenome para diferenciar"), informando a origem do
conflito.
Validado em DEV (3 homônimos "João Silva": origem distinta e mesma origem → sequencial).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
52 lines
2.5 KiB
TypeScript
52 lines
2.5 KiB
TypeScript
// Desambiguação de homônimos na bancada do protético.
|
|
// O protético identifica o trabalho pelo NOME do paciente (sem CPF — princípio de privacidade).
|
|
// Quando dois trabalhos têm o mesmo nome+sobrenome, diferenciamos sem expor dado sensível:
|
|
// 1) pela ORIGEM (dentista, senão clínica) — "João Silva · Dr. Carlos";
|
|
// 2) se ainda empatar (mesma origem), por nº sequencial estável — "João Silva · Dr. Carlos (2)".
|
|
// Etiqueta livre, nunca um cadastro/atendimento de paciente.
|
|
|
|
const DIACRITICOS = new RegExp('[' + String.fromCharCode(0x300) + '-' + String.fromCharCode(0x36f) + ']', 'g');
|
|
const norm = (s?: string) =>
|
|
(s || '').normalize('NFD').replace(DIACRITICOS, '').toLowerCase().replace(/\s+/g, ' ').trim();
|
|
|
|
const origemDe = (o: any) => (o?.dentista_nome || o?.clinica_nome || o?.cliente_nome || '').trim();
|
|
|
|
// Recebe a lista de OS e devolve um Map id → rótulo de exibição já desambiguado.
|
|
export function rotulosPaciente(lista: any[]): Map<string, string> {
|
|
const grupos = new Map<string, any[]>();
|
|
for (const o of lista || []) {
|
|
const k = norm(o?.paciente_nome);
|
|
if (!k) continue;
|
|
const g = grupos.get(k);
|
|
if (g) g.push(o); else grupos.set(k, [o]);
|
|
}
|
|
const out = new Map<string, string>();
|
|
for (const o of lista || []) {
|
|
const base = (o?.paciente_nome || '').trim() || 'Sem identificação';
|
|
const k = norm(o?.paciente_nome);
|
|
const grupo = k ? (grupos.get(k) || [o]) : [o];
|
|
if (grupo.length <= 1) { out.set(o.id, base); continue; }
|
|
const origem = origemDe(o);
|
|
const mesmaOrigem = grupo.filter(g => norm(origemDe(g)) === norm(origem));
|
|
if (origem && mesmaOrigem.length === 1) {
|
|
out.set(o.id, `${base} · ${origem}`);
|
|
} else {
|
|
// mesma origem (ou origem ausente) → sequencial estável por data de criação
|
|
const ord = [...mesmaOrigem].sort((a, b) => String(a.created_at || '').localeCompare(String(b.created_at || '')));
|
|
const idx = ord.findIndex(g => g.id === o.id) + 1;
|
|
out.set(o.id, origem ? `${base} · ${origem} (${idx})` : `${base} (${idx})`);
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
// Há outro trabalho ATIVO com o mesmo nome+sobrenome? (usado no aviso da entrada do lab)
|
|
// Retorna a origem do homônimo encontrado, ou null.
|
|
export function homonimoAtivo(nomeCompleto: string, lista: any[]): string | null {
|
|
const k = norm(nomeCompleto);
|
|
if (!k) return null;
|
|
const hit = (lista || []).find(o =>
|
|
norm(o?.paciente_nome) === k && !['entregue', 'cancelado'].includes(o?.status));
|
|
return hit ? (origemDe(hit) || 'outro cliente') : null;
|
|
}
|