Multiple call logs being stored from cdr event in Asterisk while transfering call

93 Views Asked by At

While transferring a call, the lifespan of a call throws 5 different cdr events. One from queue and other acts as an inbound call.

 this.ami.on('cdr', async (evt) => {
      if (evt.source == '//somerandomtestphonenumber//') {
        console.log(evt);
      }
      var destination: string = evt.destination.toString();

      var destinationcontext: string = evt.destinationcontext.toString();
      var disposition: string = evt.disposition.toString();
      var channel: string = evt.channel.toString();
      var destinationchannel: string = evt.destinationchannel.toString();
      var source: string = evt.source.toString();
      const landline: string = '+//someLandLinePhoneNumber';
      var receiver: string = evt.destination.toString();
      var startTime: Date = new Date(evt.starttime);
      var answerTime: Date =
        evt.answertime == '' ? null : new Date(evt.answertime);
      var endTime: Date = new Date(evt.endtime);
      var totalTime: number = parseFloat(evt.duration);
      var callTime: number = parseFloat(evt.billableseconds);
      var holdTime: number = totalTime - callTime;
      var lastapplication = evt.lastapplication.toString();
      var duration: string = evt.duration.toString();
      var billableseconds: string = evt.billableseconds.toString();
      var uniqueId: string = evt.uniqueid.toString();

      // Voicemail
      if (destination == 's' && evt.lastapplication == 'VoiceMail') {
        const contextFirstPart = destinationcontext.substring(
          destinationcontext.indexOf('-') + 1,
          destinationcontext.length,
        );
        const queue = contextFirstPart.substring(
          contextFirstPart.indexOf('-') + 1,
          contextFirstPart.length,
        );
        // Note : Event is handled in voicemail service

        this.eventEmitter.emit(
          'voicemail-received',
          new VoicemailLogDto(
            queue,
            source,
            startTime,
            answerTime,
            endTime,
            duration,
            disposition,
            uniqueId,
            channel,
          ),
        );
      }

      if (
        !destinationcontext.includes('ivr') &&
        !disposition.includes('CONGESTION') &&
        !disposition.includes('FAILED')
      ) {
        var caller: string =
          source == landline
            ? evt.channel.split('/')[1].split('-')[0]
            : evt.source.toString();

        const callLogEntry = new CreateCalllogDto(
          caller,
          receiver,
          startTime,
          answerTime,
          endTime,
          evt.endtime,
          holdTime,
          totalTime,
          callTime,
          disposition,
          'unknown',
          uniqueId,
        );

        // For Outbound
        if (source == landline && destinationcontext == 'from-internal') {
          callLogEntry.type = 'outbound';
          if (process.env.NODE_ENV !== 'development') {
            await this.callLogService.create(callLogEntry);
          }

          if (
            callLogEntry.disposition == 'NO ANSWER' ||
            callLogEntry.disposition == 'BUSY'
          ) {
            this.realtimeGateway.emitData(
              'outbound-missed',
              'outbound-missed',
              [Role.ADMIN, Role.SUPERVISOR, Role.AGENT],
            );
          }
          if (callLogEntry.disposition == 'ANSWERED') {
            this.realtimeGateway.emitData(
              'outbound-success',
              'outbound-success',
              [Role.ADMIN, Role.SUPERVISOR, Role.AGENT],
            );
          }
        }

        // For inbound
        if (source != landline && destinationcontext == 'from-internal') {
          callLogEntry.type = 'inbound';
          var result = await this.callLogService.create(callLogEntry);
        }

        // For internal
        if (destinationchannel.includes('from-internal-xfer')) {
          callLogEntry.type = 'internal';
          await this.callLogService.create(callLogEntry);
        }

        // For queue
        if (source != landline && destinationcontext == 'ext-queues') {
          const queueLogEntry = new CreateQueueReceiveLogDto(
            caller,
            destinationchannel.substring(
              destinationchannel.indexOf('/') + 1,
              destinationchannel.indexOf('@'),
            ),
            receiver,
            'ANSWERED',
            uniqueId,
            evt.endtime,
          );
          if (process.env.NODE_ENV !== 'development') {
            await this.queueLogService.createQueueReceiveLog(queueLogEntry);
          }
        }
      }
    });
  }

Here, if there is normal call (without extension) the call acts as an inbound call and the call log report is being created only once (2 cdr events are thrown, 1 for queue and 1 for the inbound) But, if there is any call transfers between extensions in the current ongoing call, then there are 5 different cdr events, 1 for queue and 4 events are for the inbound and 4 different log reports are created of a single call. Is there any possible way of creating only 1 call log report if there is any transfer going on in the lifecycle of the call?

the console.log(evt) of the cdr event with transfer is logged as :

{
  event: 'Cdr',
  privilege: 'cdr,all',
  accountcode: '',
  source: '/somerandomphonenumber/',
  destination: '4043',
  destinationcontext: 'from-internal',
  callerid: '"/somerandomphonenumber/" </somerandomphonenumber/>',
  channel: 'Local/4043@from-internal-0000032a;2',
  destinationchannel: 'SIP/4043-00000835',
  lastapplication: 'Dial',
  lastdata: 'SIP/4043,,trM(auto-blkvm)I',
  starttime: '2023-10-03 11:52:49',
  answertime: '2023-10-03 11:52:52',
  endtime: '2023-10-03 11:52:57',
  duration: '7',
  billableseconds: '4',
  disposition: 'ANSWERED',
  amaflags: 'DOCUMENTATION',
  uniqueid: '1696313269.25353',
  userfield: ''
}
{
  event: 'Cdr',
  privilege: 'cdr,all',
  accountcode: '',
  source: '/somerandomphonenumber/',
  destination: '4042',
  destinationcontext: 'from-internal',
  callerid: '"/somerandomphonenumber/" </somerandomphonenumber/>',
  channel: 'Local/4042@from-queue-00000327;2',
  destinationchannel: 'SIP/4042-0000082f',
  lastapplication: 'Dial',
  lastdata: 'SIP/4042,,trM(auto-blkvm)',
  starttime: '2023-10-03 11:52:30',
  answertime: '2023-10-03 11:52:36',
  endtime: '2023-10-03 11:52:49',
  duration: '18',
  billableseconds: '12',
  disposition: 'ANSWERED',
  amaflags: 'DOCUMENTATION',
  uniqueid: '1696313250.25287',
  userfield: ''
}
{
  event: 'Cdr',
  privilege: 'cdr,all',
  accountcode: '',
  source: '/somerandomphonenumber/',
  destination: '4042',
  destinationcontext: 'from-internal',
  callerid: '"/somerandomphonenumber/" </somerandomphonenumber/>',
  channel: 'Local/4042@from-queue-00000327;2',
  destinationchannel: 'Local/4043@from-internal-0000032a;1',
  lastapplication: 'Dial',
  lastdata: 'SIP/4042,,trM(auto-blkvm)',
  starttime: '2023-10-03 11:52:49',
  answertime: '2023-10-03 11:52:49',
  endtime: '2023-10-03 11:52:57',
  duration: '7',
  billableseconds: '7',
  disposition: 'ANSWERED',
  amaflags: 'DOCUMENTATION',
  uniqueid: '1696313250.25287',
  userfield: ''
}
{
  event: 'Cdr',
  privilege: 'cdr,all',
  accountcode: '',
  source: '/somerandomphonenumber/',
  destination: '4042',
  destinationcontext: 'from-internal',
  callerid: '"/somerandomphonenumber/" </somerandomphonenumber/>',
  channel: 'Local/4042@from-queue-00000327;2',
  destinationchannel: 'SIP/4043-00000835',
  lastapplication: 'Dial',
  lastdata: 'SIP/4042,,trM(auto-blkvm)',
  starttime: '2023-10-03 11:52:57',
  answertime: '2023-10-03 11:52:57',
  endtime: '2023-10-03 11:53:02',
  duration: '4',
  billableseconds: '4',
  disposition: 'ANSWERED',
  amaflags: 'DOCUMENTATION',
  uniqueid: '1696313250.25287',
  userfield: ''
}
{
  event: 'Cdr',
  privilege: 'cdr,all',
  accountcode: '',
  source: '/somerandomphonenumber/',
  destination: '602',
  destinationcontext: 'ext-queues',
  callerid: '"/somerandomphonenumber/" </somerandomphonenumber/>',
  channel: 'SIP/ntc_sip-0000082d',
  destinationchannel: 'Local/4042@from-queue-00000327;1',
  lastapplication: 'Queue',
  lastdata: '602,t,,,10,,,,,',
  starttime: '2023-10-03 11:52:27',
  answertime: '2023-10-03 11:52:27',
  endtime: '2023-10-03 11:53:02',
  duration: '34',
  billableseconds: '34',
  disposition: 'ANSWERED',
  amaflags: 'DOCUMENTATION',
  uniqueid: '1696313247.25273',
  userfield: ''
}
1

There are 1 best solutions below

0
arheops On

To understand that, you have to understand how dialplan on your PBX is organized. As I understand from record it is Freepbx or clone.

Asterisk does store CDR records not per call, but per channel used. Except it eliminates outgoing channels in this scheme.

So, if you are using Local/ channel, you have two originating channels and two CDRs.

When you do attended transfer your phone MAY create another(third) channel to destination first, speak with it, after that bridge old channel.

You can eliminate some channels by issuing NoCDR command on it, but it is complex and will not work with some attended transfer/hardware phones.

Queue calls have one incoming channel, and unlimited (can be easy 100+) outgoing channels related to call. That is how it is working.

In most cases, you can connect all those CDRs by linkedid/linkeduniq field.