feat(rotas-entrada): dropdown de destino real (ramal/IVR/fila/grupo)
Pedido do usuário: "em vez de o campo de destino ser aberto tem que ter um dropdown listando todos os ramais do tennant e tb todas ivr e filas e grupos de ramais do tennant" — até aqui o destino era texto livre. Construir o dropdown revelou que fila e grupo NUNCA tinham sido implementados de verdade como destino possível — só ramal/IVR funcionavam. Oferecer as duas opções sem o mecanismo por trás seria mostrar um dropdown mentiroso, então: InboundRoute.destinationType (novo enum EXTENSION/IVR/QUEUE/CALL_GROUP) decide como buildInboundRouteXml interpreta o destino: - EXTENSION/IVR: inalterado, o mesmo transfer já testado (PHASE 56/58). - QUEUE: destinationNumber guarda o Queue.id; a resolução de entrada emite `answer` + `callcenter data="<queueId>@<domain>"` direto, sem tocar no dialplan "default". `callcenter` entrou no allowlist de applications com o mesmo risco zero de `pickup`. - CALL_GROUP: destinationNumber guarda o Extension.callGroup; a resolução consulta AGORA (nunca um snapshot salvo) todos os ramais com esse callGroup e emite um `bridge` multi-leg — toca todos ao mesmo tempo, quem atender primeiro cancela os outros. Trocar quem está no grupo depois de criar a rota já vale na próxima chamada. Testado ponta a ponta com chamadas reais nos 2 mecanismos novos: fila — softphone externo discou o DID, show channels confirmou a chamada dentro da application callcenter com o nome certo da fila; grupo — 2 ramais reais no mesmo callGroup, a chamada tocou nos DOIS ao mesmo tempo (mesmo call_uuid, ambas RINGING), atender em um cancelou o outro automaticamente — ring group de verdade. Tela: "Tipo de destino" + um segundo dropdown com as opções reais do tenant pra cada tipo (ramais, menus de IVR, filas, ou os valores distintos de callGroup já usados em algum ramal). Testado com Playwright: os 4 tipos aparecem, e trocar o tipo atualiza as opções do segundo dropdown corretamente. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
@@ -108,9 +108,34 @@ async function resolveInboundRouteXml(didNumber: string | undefined): Promise<st
|
||||
tx.inboundRoute.findFirst({ where: { tenantId: tenant.id, didNumber, enabled: true, deletedAt: null } }),
|
||||
);
|
||||
if (route) {
|
||||
const domain = tenant.telephonyDomain!;
|
||||
|
||||
// PHASE 62: fila e grupo nunca "discam um número" no dialplan do
|
||||
// tenant — a resolução de entrada já emite a action final.
|
||||
if (route.destinationType === "QUEUE") {
|
||||
return buildInboundRouteXml({ tenantId: tenant.id, domain, destinationType: "QUEUE", queueId: route.destinationNumber });
|
||||
}
|
||||
if (route.destinationType === "CALL_GROUP") {
|
||||
// Resolvido AGORA, nunca um snapshot salvo — trocar quem está no
|
||||
// grupo depois de criar a rota já vale na PRÓXIMA chamada.
|
||||
const members = await withTenantContext(prisma, tenant.id, (tx) =>
|
||||
tx.extension.findMany({
|
||||
where: { tenantId: tenant.id, callGroup: route.destinationNumber, enabled: true, deletedAt: null },
|
||||
select: { number: true },
|
||||
}),
|
||||
);
|
||||
return buildInboundRouteXml({
|
||||
tenantId: tenant.id,
|
||||
domain,
|
||||
destinationType: "CALL_GROUP",
|
||||
groupMembers: members.map((m) => m.number),
|
||||
});
|
||||
}
|
||||
|
||||
return buildInboundRouteXml({
|
||||
tenantId: tenant.id,
|
||||
domain: tenant.telephonyDomain!,
|
||||
domain,
|
||||
destinationType: route.destinationType,
|
||||
destinationNumber: route.destinationNumber,
|
||||
destinationContext: route.destinationContext,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user