mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage-server
synced 2026-05-18 20:08:19 +00:00
fix(call-attribution): resolve Ozonetel chain AgentNames to agent.id
Inbound transferred calls arrive with AgentName like 'RamaiahAdmin -> GlobalHealthX'. The webhook was persisting the raw chain string and leaving agentId null; the CDR enrichment cron then silently skipped 100% of rows because the bulk CDR keys on caller-leg UCID while the webhook stores monitorUCID — the join never matched. - missed-call-webhook: split chain on ' -> ', take final handler, resolve via AgentLookupService (ozonetelAgentId + display name) - cdr-enrichment: index CDR rows by both UCID and monitorUCID so the cron actually patches historical rows - enrichment also parses chain in CDR AgentName as a second fallback - spec: add CallerResolutionService + AgentLookupService mocks
This commit is contained in:
@@ -63,10 +63,18 @@ export class CdrEnrichmentService implements OnModuleInit, OnModuleDestroy {
|
||||
if (cdrRows.length === 0) continue;
|
||||
|
||||
// Build UCID → cdr-row map so we can O(1) join per Call.
|
||||
// Ozonetel emits two identifiers per call — `UCID` (caller-leg)
|
||||
// and `monitorUCID` (agent-leg). The webhook stores `monitorUCID`,
|
||||
// but the bulk CDR rows are keyed on caller-leg `UCID`. Index
|
||||
// both so the lookup at line ~79 finds the row regardless of
|
||||
// which side was persisted. Without this, transferred inbound
|
||||
// calls never get their agent relation enriched.
|
||||
const byUcid = new Map<string, any>();
|
||||
for (const row of cdrRows) {
|
||||
const ucid = String(row.UCID ?? '').trim();
|
||||
const monitorUcid = String(row.monitorUCID ?? '').trim();
|
||||
if (ucid) byUcid.set(ucid, row);
|
||||
if (monitorUcid && monitorUcid !== ucid) byUcid.set(monitorUcid, row);
|
||||
}
|
||||
if (byUcid.size === 0) continue;
|
||||
|
||||
@@ -80,9 +88,25 @@ export class CdrEnrichmentService implements OnModuleInit, OnModuleDestroy {
|
||||
if (!cdrRow) { skipped++; continue; }
|
||||
|
||||
const patch: Record<string, any> = {};
|
||||
const cdrAgentId = cdrRow.AgentID;
|
||||
if (cdrAgentId && !call.agentId) {
|
||||
const uuid = await this.agentLookup.resolveByOzonetelId(cdrAgentId);
|
||||
if (!call.agentId) {
|
||||
// Primary resolution: use AgentID from CDR (unique lowercase id).
|
||||
const cdrAgentId = cdrRow.AgentID;
|
||||
let uuid = cdrAgentId
|
||||
? await this.agentLookup.resolveByOzonetelId(cdrAgentId)
|
||||
: null;
|
||||
// Fallback: CDR AgentName may be a chain ("A -> B") for
|
||||
// transferred calls. Pick the final handler (last segment)
|
||||
// and look it up by display name or ozonetelId. Matches
|
||||
// the write-time resolution in missed-call-webhook.
|
||||
if (!uuid && cdrRow.AgentName) {
|
||||
const segments = String(cdrRow.AgentName).split('->').map((s) => s.trim()).filter(Boolean);
|
||||
const finalHandler = segments[segments.length - 1];
|
||||
if (finalHandler) {
|
||||
uuid =
|
||||
(await this.agentLookup.resolveByOzonetelId(finalHandler)) ??
|
||||
(await this.agentLookup.resolveByDisplayName(finalHandler));
|
||||
}
|
||||
}
|
||||
if (uuid) patch.agentId = uuid;
|
||||
if (cdrRow.AgentName) patch.agentName = cdrRow.AgentName;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user