fix: outbound call records via dispose + campaign-filtered polling

- Dispose endpoint creates Call entity for outbound calls (direction=OUTBOUND).
  The webhook now skips outbound, so dispose is the only path for outbound records.
- MissedQueueService filters abandonCalls by own campaign (read from TelephonyConfigService).
  Prevents cross-tenant ingestion from shared Ozonetel account.
- WorklistModule provides TelephonyConfigService directly (avoids circular dep with ConfigThemeModule).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-10 16:20:45 +05:30
parent 96d0c32000
commit c807cf737f

View File

@@ -146,6 +146,37 @@ export class OzonetelAgentController {
this.logger.error(`[DISPOSE] FAILED: ${message} ${responseData}`); this.logger.error(`[DISPOSE] FAILED: ${message} ${responseData}`);
} }
// Create call record for outbound calls. Inbound calls are
// created by the webhook — but we skip outbound in the webhook
// (they're not "missed calls"). So the dispose endpoint is the
// only place that creates the call record for outbound dials.
if (body.direction === 'OUTBOUND' && body.callerPhone) {
try {
const callData: Record<string, any> = {
name: `Outbound — ${body.callerPhone}`,
direction: 'OUTBOUND',
callStatus: 'COMPLETED',
callerNumber: { primaryPhoneNumber: body.callerPhone },
agentName: agentId,
durationSec: body.durationSec ?? 0,
disposition: body.disposition,
};
if (body.leadId) callData.leadId = body.leadId;
const apiKey = process.env.PLATFORM_API_KEY;
if (apiKey) {
const result = await this.platform.queryWithAuth<any>(
`mutation($data: CallCreateInput!) { createCall(data: $data) { id } }`,
{ data: callData },
`Bearer ${apiKey}`,
);
this.logger.log(`[DISPOSE] Created outbound call record: ${result.createCall.id}`);
}
} catch (err: any) {
this.logger.warn(`[DISPOSE] Failed to create outbound call record: ${err.message}`);
}
}
// Handle missed call callback status update // Handle missed call callback status update
if (body.missedCallId) { if (body.missedCallId) {
const statusMap: Record<string, string> = { const statusMap: Record<string, string> = {