Your IP : 13.58.182.39


Current Path : /home/bitrix/ext_www/crm.klimatlend.ua/bitrix/modules/voximplant/lib/
Upload File :
Current File : /home/bitrix/ext_www/crm.klimatlend.ua/bitrix/modules/voximplant/lib/call.php

<?php

namespace Bitrix\Voximplant;

use Bitrix\Main\ArgumentException;
use Bitrix\Main\SystemException;
use Bitrix\Main\Type\DateTime;
use Bitrix\Voximplant\Model\CallCrmEntityTable;
use Bitrix\Voximplant\Model\CallTable;
use Bitrix\Voximplant\Model\CallUserTable;
use Bitrix\Voximplant\Routing\Node;

class Call
{
	protected $id;
	protected $configId;
	protected $userId;
	protected $portalUserId;
	protected $callId;
	protected $externalCallId;
	protected $incoming;
	protected $callerId;
	protected $status;
	protected $crm;
	protected $crmActivityId;
	protected $crmCallList;
	protected $crmBindings;
	protected $crmEntities;
	protected $accessUrl;
	protected $dateCreate;
	protected $restAppId;
	protected $externalLineId;
	protected $portalNumber;
	protected $stage;
	protected $ivrActionId;
	protected $queueId;
	protected $queueHistory = [];
	protected $sessionId;
	protected $callbackParameters;
	protected $comment;
	protected $worktimeSkipped = false;
	protected $sipHeaders = [];
	protected $gatheredDigits;
	protected $parentCallId;
	protected $lastPingDate;
	protected $executionGraph;

	protected $users = [];

	protected $signaling;
	protected $scenario;
	protected static $instances = [];
	protected static $externalInstances = [];

	protected $config;

	/**
	 * Call constructor. Do not use directly, use static constructor create or load instead.
	 */
	protected function __construct()
	{
		$this->signaling = new Signaling($this);
		$this->scenario = new Scenario($this);
	}

	/**
	 * Loads call from database.
	 *
	 * @param string $callId Id of the call.
	 * @return Call | false
	 */
	public static function load($callId)
	{
		if(static::$instances[$callId])
		{
			return static::$instances[$callId];
		}

		$fields = CallTable::getByCallId($callId);
		if(!$fields)
		{
			return false;
		}

		$users = [];
		$cursor = CallUserTable::getList([
			'select' => ['USER_ID', 'ROLE', 'STATUS'],
			'filter' => ['=CALL_ID' => $callId]
		]);

		while ($row = $cursor->fetch())
		{
			$users[$row['USER_ID']] = $row;
		}

		$instance = new static();
		static::$instances[$callId] = $instance;

		$instance->fromArray($fields);
		$instance->users = $users;
		return $instance;
	}

	public static function loadExternal($externalCallId)
	{
		if(static::$externalInstances[$externalCallId])
		{
			return static::$externalInstances[$externalCallId];
		}

		$fields = CallTable::getRow([
			'filter' => [
				'=EXTERNAL_CALL_ID' => $externalCallId
			]
		]);
		if(!$fields)
		{
			return false;
		}

		$users = [];
		$cursor = CallUserTable::getList([
			'select' => ['USER_ID', 'ROLE', 'STATUS'],
			'filter' => ['=CALL_ID' => $fields['CALL_ID']]
		]);

		while ($row = $cursor->fetch())
		{
			$users[$row['USER_ID']] = $row;
		}

		$instance = new static();
		static::$externalInstances[$externalCallId] = $instance;

		$instance->fromArray($fields);
		$instance->users = $users;
		return $instance;
	}

	/**
	 * Create new call with specified fields.
	 *
	 * @param array $fields
	 * @return Call
	 */
	public static function create(array $fields): Call
	{
		static::checkFields($fields);

		if($fields['CONFIG_ID'])
		{
			$config = \CVoxImplantConfig::GetConfig($fields['CONFIG_ID']);
		}

		if(!$fields['DATE_CREATE'])
		{
			$fields['DATE_CREATE'] = new DateTime();
		}
		if(!$fields['LAST_PING'])
		{
			$fields['LAST_PING'] = new DateTime();
		}

		$fields['CRM'] = 'Y';

		if(!$fields['QUEUE_ID'])
		{
			if(isset($config['QUEUE_ID']))
			{
				$fields['QUEUE_ID'] = $config['QUEUE_ID'];
			}
			else
			{
				$fields['QUEUE_ID'] = \CVoxImplantMain::getDefaultGroupId();
			}
		}

		$instance = new static();
		$instance->config = $config;
		$instance->fromArray($fields);
		$instance->save();
		static::$instances[$fields['CALL_ID']] = $instance;
		return $instance;
	}

	/**
	 * @return int
	 */
	public function getId()
	{
		return $this->id;
	}

	/**
	 * @return string
	 */
	public function getCallId()
	{
		return $this->callId;
	}

	/**
	 * @return string|null
	 */
	public function getExternalCallId()
	{
		return $this->externalCallId;
	}

	/**
	 * @return mixed
	 */
	public function getParentCallId()
	{
		return $this->parentCallId;
	}

	/**
	 * @return mixed
	 */
	public function getIncoming()
	{
		return $this->incoming;
	}

	/**
	 * @return int
	 */
	public function getUserId()
	{
		return $this->userId;
	}

	/**
	 * @param int $userId
	 */
	public function updateUserId($userId)
	{
		$this->update(['USER_ID' => $userId]);
	}

	/**
	 * @return int
	 */
	public function getPortalUserId()
	{
		return $this->portalUserId;
	}

	/**
	 * @param int $portalUserId
	 */
	public function updatePortalUserId($portalUserId)
	{
		$this->update(['PORTAL_USER_ID' => $portalUserId]);
	}

	public function isInternalCall()
	{
		return $this->portalUserId != '';
	}

	/**
	 * @return string
	 */
	public function getStage()
	{
		return $this->stage;
	}

	/**
	 * @param string $stage
	 */
	public function updateStage($stage)
	{
		$this->update(['STAGE' => $stage]);
	}

	/**
	 * @return int
	 */
	public function getQueueId()
	{
		return $this->queueId;
	}

	/**
	 * @return int
	 */
	public function getIvrActionId()
	{
		return $this->ivrActionId;
	}

	/**
	 * @param int $ivrActionId
	 */
	public function updateIvrActionId($ivrActionId)
	{
		$this->update(['IVR_ACTION_ID' => $ivrActionId]);
	}

	/**
	 * @return Node|null
	 */
	public function getExecutionGraph()
	{
		return $this->executionGraph;
	}

	/**
	 * @param Node $executionGraph
	 */
	public function updateExecutionGraph(Node $executionGraph)
	{
		$this->update(['EXECUTION_GRAPH' => $executionGraph]);
	}

	/**
	 * @param int $queueId
	 */
	public function moveToQueue($queueId)
	{
		$this->update([
			'QUEUE_ID' => $queueId,
			'QUEUE_HISTORY' => []
		]);
	}

	/**
	 * @param $userId
	 */
	public function moveToUser($userId)
	{
		$invitedUsers = array_filter($this->users, function ($user)
		{
			return ($user['STATUS'] == CallUserTable::STATUS_INVITING);
		});

		if(count($invitedUsers) > 0)
		{
			$this->removeUsers(array_keys($invitedUsers));
		}

		$fields = [
			'USER_ID' => $userId,
		];
		$queueHistory = $this->queueHistory;
		if(!in_array($userId, $queueHistory))
		{
			$queueHistory[] = $userId;
			$fields['QUEUE_HISTORY'] = $queueHistory;
		}
		$this->update($fields);
		$this->addUsers([$userId], CallUserTable::ROLE_CALLEE);
	}


	/**
	 * @return string
	 */
	public function getSessionId()
	{
		return $this->sessionId;
	}

	/**
	 * @param string $sessionId
	 */
	public function updateSessionId($sessionId)
	{
		$this->update(['SESSION_ID' => $sessionId]);
	}

	/**
	 * @return mixed
	 */
	public function getComment()
	{
		return $this->comment;
	}

	/**
	 * @return bool
	 */
	public function isWorktimeSkipped()
	{
		return $this->worktimeSkipped;
	}

	/**
	 * @return mixed
	 */
	public function getConfig()
	{
		return $this->config;
	}

	/**
	 * @return string
	 */
	public function getCallerId()
	{
		return $this->callerId;
	}

	/**
	 * @return mixed
	 */
	public function getPortalNumber()
	{
		return $this->portalNumber;
	}

	/**
	 * @return string
	 */
	public function getAccessUrl()
	{
		return $this->accessUrl;
	}

	/**
	 * @return DateTime
	 */
	public function getDateCreate()
	{
		return $this->dateCreate;
	}

	/**
	 * @return mixed
	 */
	public function getRestAppId()
	{
		return $this->restAppId;
	}

	/**
	 * @return mixed
	 */
	public function getExternalLineId()
	{
		return $this->externalLineId;
	}

	/**
	 * Sets comment for the call.
	 *
	 * @param string $comment
	 * @return void
	 */
	public function setComment($comment)
	{
		$this->update(['COMMENT' => $comment]);
	}

	/**
	 * @return string
	 */
	public function getStatus()
	{
		return $this->status;
	}

	/**
	 * Updates call status.
	 * @param string $status
	 */
	public function updateStatus($status)
	{
		$fields = ['STATUS' => $status];
		if($status == CallTable::STATUS_ENQUEUED)
		{
			$fields['QUEUE_HISTORY'] = [];
		}
		$this->update($fields);
	}

	/**
	 * Returns true if this call should be associated with crm.
	 * @return bool
	 */
	public function isCrmEnabled()
	{
		return true;
	}

	/**
	 * @return int
	 */
	public function getCrmActivityId()
	{
		return $this->crmActivityId;
	}

	/**
	 * @return mixed
	 */
	public function getCrmCallList()
	{
		return $this->crmCallList;
	}

	/**
	 * @param int $crmActivityId
	 */
	public function setCrmActivityId($crmActivityId)
	{
		$this->update(['CRM_ACTIVITY_ID' => $crmActivityId]);
	}

	public function getCrmEntities()
	{
		$this->loadCrmEntities();

		return $this->crmEntities;
	}

	/**
	 * @return array
	 */
	public function getCreatedCrmEntities()
	{
		$this->loadCrmEntities();

		$result = [];

		foreach ($this->crmEntities as $entity)
		{
			if($entity['IS_CREATED'] == 'Y')
			{
				$result[] = $entity;
			}
		}
		return $result;
	}

	public function getCreatedCrmLead()
	{
		$this->loadCrmEntities();

		$result = [];

		foreach ($this->crmEntities as $entity)
		{
			if($entity['IS_CREATED'] == 'Y' && $entity['ENTITY_TYPE'] === 'LEAD')
			{
				return (int)$entity['ENTITY_ID'];
			}
		}
		return $result;
	}

	public function updateCrmEntities(array $newEntities)
	{
		$this->crmEntities = $newEntities;
		foreach ($this->crmEntities as $entity)
		{
			CallCrmEntityTable::merge([
				'CALL_ID' => $this->callId,
				'ENTITY_TYPE' => $entity['ENTITY_TYPE'],
				'ENTITY_ID' => $entity['ENTITY_ID'],
				'IS_PRIMARY' => $entity['IS_PRIMARY'],
				'IS_CREATED' => $entity['IS_CREATED'],
			]);
		}
	}

	public function addCrmEntities(array $newEntities)
	{
		$this->loadCrmEntities();

		$this->updateCrmEntities(array_merge($this->crmEntities, $newEntities));
	}

	/**
	 * @return string|false
	 */
	public function getPrimaryEntityType()
	{
		$this->loadCrmEntities();

		foreach ($this->crmEntities as $entity)
		{
			if($entity['IS_PRIMARY'] == 'Y')
			{
				return $entity['ENTITY_TYPE'];
			}
		}
		return false;
	}

	/**
	 * @return int|false
	 */
	public function getPrimaryEntityId()
	{
		$this->loadCrmEntities();

		foreach ($this->crmEntities as $entity)
		{
			if($entity['IS_PRIMARY'] == 'Y')
			{
				return (int)$entity['ENTITY_ID'];
			}
		}
		return false;
	}

	/**
	 * @return array
	 */
	public function getCrmBindings()
	{
		return $this->crmBindings;
	}

	/**
	 * @param array $crmBindings
	 */
	public function updateCrmBindings(array $crmBindings)
	{
		$this->update(['CRM_BINDINGS' => $crmBindings]);
	}

	/**
	 * @return array
	 */
	public function getUsers()
	{
		return $this->users;
	}

	public function getUserIds()
	{
		return array_keys($this->users);
	}

	/**
	 * Adds users to the call. Does not automatically sends invite to prevent synchronisation issues.
	 * Invite will be sent late, with the scenario request.
	 *
	 * @param int[] $users Array of user ids.
	 * @param string $role Role of users in the call.
	 * @params string $status User connection status.
	 * @return void
	 */
	public function addUsers(array $users, $role, $status = CallUserTable::STATUS_INVITING)
	{
		foreach ($users as $userId)
		{
			$userRecord = [
				'USER_ID' => $userId,
				'ROLE' => $role,
				'STATUS' => $status,
				'INSERTED' => new DateTime()
			];
			$this->users[$userId] = $userRecord;

			$dbRecord = $userRecord;
			$dbRecord['CALL_ID'] = $this->callId;
			CallUserTable::merge($dbRecord);
		}
	}

	/**
	 * @param array $users
	 * @param bool $sendTimeout
	 * @throws \Exception
	 */
	public function removeUsers(array $users, $sendTimeout = true)
	{
		foreach ($users as $userId)
		{
			CallUserTable::delete([
				'CALL_ID' => $this->callId,
				'USER_ID' => $userId
			]);

			unset($this->users[$userId]);
		}
		if(count($users) > 0)
		{
			if($sendTimeout)
			{
				$this->signaling->sendTimeout($users);
			}
			foreach ($users as $userId)
			{
				$userInfo = \CVoxImplantIncoming::getUserInfo($userId);
				if($userInfo['AVAILABLE'] == 'Y')
				{
					CallQueue::dequeueFirstUserCall($userId);
				}
			}
		}
	}

	public function removeAllInvitedUsers()
	{
		$usersToRemove = [];
		foreach ($this->users as $userId => $user)
		{
			if($user['STATUS'] == CallUserTable::STATUS_INVITING || $user['STATUS'] == CallUserTable::STATUS_CONNECTING)
			{
				$usersToRemove[] = $userId;
			}
		}

		if(count($usersToRemove) > 0)
		{
			$this->removeUsers($usersToRemove);
		}
	}

	public function updateUserStatus($userId, $status, $device = '')
	{
		if(!isset($this->users[$userId]))
		{
			throw new SystemException("User is not participant of the call");
		}

		$this->users[$userId]['STATUS'] = $status;
		if($device)
		{
			$this->users[$userId]['DEVICE'] = $device;
		}

		CallUserTable::update(['CALL_ID' => $this->callId, 'USER_ID' => $userId], [
			'STATUS' => $this->users[$userId]['STATUS'],
			'DEVICE' => $this->users[$userId]['DEVICE']
		]);
	}

	/**
	 * @return array
	 */
	public function getQueueHistory()
	{
		return $this->queueHistory;
	}

	public function updateQueueHistory(array $queueHistory)
	{
		$this->update(['QUEUE_HISTORY' => $queueHistory]);
	}

	public function addToQueueHistory(array $users)
	{
		foreach ($users as $userId)
		{
			if(!in_array($userId, $this->queueHistory))
			{
				$this->queueHistory[] = $userId;
			}
		}
		CallTable::update($this->id, ['QUEUE_HISTORY' => $this->queueHistory]);
	}

	public function clearQueueHistory()
	{
		$this->update(['QUEUE_HISTORY' => []]);
	}

	/**
	 * @param string $headerName
	 * @return mixed
	 */
	public function getSipHeader($headerName)
	{
		return isset($this->sipHeaders[$headerName]) ? $this->sipHeaders[$headerName] : null;
	}

	/**
	 * @param array $sipHeaders
	 */
	public function updateSipHeaders(array $sipHeaders)
	{
		$this->update(['SIP_HEADERS' => $sipHeaders]);
	}

	/**
	 * @return mixed
	 */
	public function getGatheredDigits()
	{
		return $this->gatheredDigits;
	}

	/**
	 * @param mixed $gatheredDigits
	 */
	public function updateGatheredDigits($gatheredDigits)
	{
		$this->update(['GATHERED_DIGITS' => $gatheredDigits]);
	}

	/**
	 * @return Signaling
	 */
	public function getSignaling()
	{
		return $this->signaling;
	}

	/**
	 * @return Scenario
	 */
	public function getScenario()
	{
		return $this->scenario;
	}

	/**
	 * @return mixed
	 */
	public function getLastPingDate()
	{
		return $this->lastPingDate;
	}

	/**
	 * @param DateTime $lastPingDate
	 */
	public function updateLastPingDate(DateTime $lastPingDate)
	{
		$this->update(['LAST_PING' => $lastPingDate]);
	}

	public function dequeue($userId)
	{
		$this->updateStatus(CallTable::STATUS_WAITING);
		$this->addUsers([$userId], CallUserTable::ROLE_CALLEE);

		$commandResult = $this->scenario->sendDequeue($userId, true);

		if(!$commandResult) {
			$this->finish();
		}

		return $commandResult;
	}

	public function handleUserAnswer($userId)
	{
		$this->updateUserStatus($userId, CallUserTable::STATUS_CONNECTING);
		$this->updateStatus(Model\CallTable::STATUS_CONNECTING);

		$this->signaling->sendAnswerSelf($userId);
		$this->scenario->sendAnswer($userId);
	}

	public function handleUserConnected($userId, $device)
	{
		$updatedFields = [
			'STATUS' => Model\CallTable::STATUS_CONNECTED
		];

		if(isset($this->users[$userId]))
		{
			$this->updateUserStatus($userId, CallUserTable::STATUS_CONNECTED, $device);

			if(in_array($this->incoming, [\CVoxImplantMain::CALL_INCOMING, \CVoxImplantMain::CALL_INCOMING_REDIRECT, \CVoxImplantMain::CALL_CALLBACK]))
			{
				$updatedFields['USER_ID'] = $userId;

				\CVoxImplantCrmHelper::updateCrmEntities($this->getCreatedCrmEntities(), ['ASSIGNED_BY_ID' => $userId]);
			}
		}
		$this->update($updatedFields);

		$userToRemove = [];
		foreach ($this->users as $userId => $user)
		{
			if ($user['STATUS'] == CallUserTable::STATUS_INVITING || $user['STATUS'] == CallUserTable::STATUS_CONNECTING)
			{
				$userToRemove[] = $userId;
			}
			else if ($user['STATUS'] == CallUserTable::STATUS_CONNECTED)
			{
				$this->signaling->sendStart($userId, $user['DEVICE']);
			}
		}

		$this->removeUsers($userToRemove);
	}

	/**
	 * Finishes current call.
	 *
	 * @param array $additionalParams Additional params to pass to timeout event.
	 * @return void
	 */
	public function finish(array $additionalParams = [])
	{
		$childCalls = static::getChildCalls($this->getCallId());
		foreach ($childCalls as $childCallId)
		{
			$childCall = Call::load($childCallId);
			if($childCall)
			{
				$childCall->finish();
			}
		}

		if($this->status == CallTable::STATUS_FINISHED)
		{
			return;
		}

		if($additionalParams['externalHangup'])
		{
			static::delete($this->callId);
		}
		else
		{
			$this->update(['STATUS' => CallTable::STATUS_FINISHED]);
		}

		$users = array_keys($this->users);
		if(count($users) > 0)
		{
			$this->signaling->sendTimeout($users, $additionalParams);
		}

		foreach ($users as $userId)
		{
			$userInfo = \CVoxImplantIncoming::getUserInfo($userId);
			if($userInfo['AVAILABLE'] == 'Y')
			{
				CallQueue::dequeueFirstUserCall($userId);
			}
		}
	}

	/**
	 * @return void
	 */
	protected function loadCrmEntities()
	{
		if(is_null($this->crmEntities))
		{
			$result = CallCrmEntityTable::getList([
				'filter' => [
					'=CALL_ID' => $this->callId
				]
			])->fetchAll();

			$this->crmEntities = $result ?: [];
		}
	}

	/**
	 * Stores call in the database
	 *
	 * @return void
	 */
	protected function save()
	{
		if($this->id)
		{
			CallTable::update($this->id, $this->toArray());
		}
		else
		{
			$insertResult = CallTable::add($this->toArray());
			$this->id = $insertResult->getId();
		}
	}

	/**
	 * Performs update of the call fields.
	 *
	 * @param array $fields Call fields.
	 * @internal
	 * @throws \Exception
	 */
	public function update(array $fields)
	{
		$updateResult = CallTable::update($this->id, $fields);

		if($updateResult->isSuccess())
		{
			$updateData = $updateResult->getData();
			$this->fromArray($updateData);
		}
	}

	public static function delete($callId)
	{
		$childCalls = static::getChildCalls($callId);
		$callsToDelete = array_merge($childCalls, [$callId]);

		CallUserTable::deleteBatch([
			'=CALL_ID' => $callsToDelete
		]);

		foreach ($callsToDelete as $deletedCallId)
		{
			$callInstance = static::load($deletedCallId);
			if($callInstance)
			{
				CallTable::delete($callInstance->getId());
			}
			unset(static::$instances[$deletedCallId]);
		}
	}

	/**
	 * Returns array of the child calls ids.
	 * @param string $callId Id of the current call.
	 * @return string[]
	 * @throws ArgumentException
	 * @throws SystemException
	 * @throws \Bitrix\Main\ObjectPropertyException
	 */
	public static function getChildCalls($callId)
	{
		$cursor = CallTable::getList([
			'select' => ['CALL_ID'],
			'filter' => [
				'=PARENT_CALL_ID' => $callId
			]
		]);

		$result = [];
		while ($row = $cursor->fetch())
		{
			$result[] = $row['CALL_ID'];
		}
		return $result;
	}

	/**
	 * Validates new call fields.
	 *
	 * @param array $fields Call fields.
	 * @throws ArgumentException
	 */
	protected static function checkFields(array $fields)
	{
		if(!$fields['CALL_ID'])
		{
			throw new ArgumentException('CALL_ID is not specified');
		}

		if(!$fields['INCOMING'])
		{
			throw new ArgumentException('INCOMING is not specified');
		}
	}

	/**
	 * Converts call to array
	 *
	 * @return array
	 */
	public function toArray()
	{
		return [
			'ID' => $this->id,
			'CONFIG_ID' => $this->configId,
			'USER_ID' => $this->userId,
			'PORTAL_USER_ID' => $this->portalUserId,
			'CALL_ID' => $this->callId,
			'EXTERNAL_CALL_ID' => $this->externalCallId,
			'INCOMING' => $this->incoming,
			'CALLER_ID' => $this->callerId,
			'STATUS' => $this->status,
			'CRM' => $this->crm,
			'CRM_ACTIVITY_ID' => $this->crmActivityId,
			'CRM_CALL_LIST' => $this->crmCallList,
			'CRM_BINDINGS' => $this->crmBindings,
			'ACCESS_URL' => $this->accessUrl,
			'DATE_CREATE' => $this->dateCreate,
			'REST_APP_ID' => $this->restAppId,
			'EXTERNAL_LINE_ID' => $this->externalLineId,
			'PORTAL_NUMBER' => $this->portalNumber,
			'STAGE' => $this->stage,
			'IVR_ACTION_ID' => $this->ivrActionId,
			'QUEUE_ID' => $this->queueId,
			'QUEUE_HISTORY' => $this->queueHistory,
			'SESSION_ID' => $this->sessionId,
			'CALLBACK_PARAMETERS' => $this->callbackParameters,
			'COMMENT' => $this->comment,
			'WORKTIME_SKIPPED' => $this->worktimeSkipped ? 'Y' : 'N',
			'SIP_HEADERS' => $this->sipHeaders,
			'GATHERED_DIGITS' => $this->gatheredDigits,
			'PARENT_CALL_ID' => $this->parentCallId,
			'LAST_PING' => $this->lastPingDate,
			'EXECUTION_GRAPH' => $this->executionGraph,
		];
	}

	/**
	 * @param array $fields
	 * @return void
	 */
	protected function fromArray(array $fields)
	{
		$this->id = array_key_exists('ID', $fields) ? $fields['ID'] : $this->id;
		$this->configId = array_key_exists('CONFIG_ID', $fields) ? $fields['CONFIG_ID'] : $this->configId;
		$this->userId = array_key_exists('USER_ID', $fields) ? $fields['USER_ID'] : $this->userId;
		$this->portalUserId = array_key_exists('PORTAL_USER_ID', $fields) ? $fields['PORTAL_USER_ID'] : $this->portalUserId;
		$this->callId = array_key_exists('CALL_ID', $fields) ? $fields['CALL_ID'] : $this->callId;
		$this->externalCallId = array_key_exists('EXTERNAL_CALL_ID', $fields) ? $fields['EXTERNAL_CALL_ID'] : $this->externalCallId;
		$this->incoming = array_key_exists('INCOMING', $fields) ? $fields['INCOMING'] : $this->incoming;
		$this->callerId = array_key_exists('CALLER_ID', $fields) ? $fields['CALLER_ID'] : $this->callerId;
		$this->status = array_key_exists('STATUS', $fields) ? $fields['STATUS'] : $this->status;
		$this->crm = array_key_exists('CRM', $fields) ? $fields['CRM'] : $this->crm;
		$this->crmActivityId = array_key_exists('CRM_ACTIVITY_ID', $fields) ? $fields['CRM_ACTIVITY_ID'] : $this->crmActivityId;
		$this->crmCallList = array_key_exists('CRM_CALL_LIST', $fields) ? $fields['CRM_CALL_LIST'] : $this->crmCallList;
		$this->crmBindings = array_key_exists('CRM_BINDINGS', $fields) ? $fields['CRM_BINDINGS'] : $this->crmBindings;
		$this->accessUrl = array_key_exists('ACCESS_URL', $fields) ? $fields['ACCESS_URL'] : $this->accessUrl;
		$this->dateCreate = array_key_exists('DATE_CREATE', $fields) ? $fields['DATE_CREATE'] : $this->dateCreate;
		$this->restAppId = array_key_exists('REST_APP_ID', $fields) ? $fields['REST_APP_ID'] : $this->restAppId;
		$this->externalLineId = array_key_exists('EXTERNAL_LINE_ID', $fields) ? $fields['EXTERNAL_LINE_ID'] : $this->externalLineId;
		$this->portalNumber = array_key_exists('PORTAL_NUMBER', $fields) ? $fields['PORTAL_NUMBER'] : $this->portalNumber;
		$this->stage = array_key_exists('STAGE', $fields) ? $fields['STAGE'] : $this->stage;
		$this->ivrActionId = array_key_exists('IVR_ACTION_ID', $fields) ? $fields['IVR_ACTION_ID'] : $this->ivrActionId;
		$this->queueId = array_key_exists('QUEUE_ID', $fields) ? $fields['QUEUE_ID'] : $this->queueId;
		$this->queueHistory = array_key_exists('QUEUE_HISTORY', $fields) ? $fields['QUEUE_HISTORY'] : $this->queueHistory;
		$this->sessionId = array_key_exists('SESSION_ID', $fields) ? $fields['SESSION_ID'] : $this->sessionId;
		$this->callbackParameters = array_key_exists('CALLBACK_PARAMETERS', $fields) ? $fields['CALLBACK_PARAMETERS'] : $this->callbackParameters;
		$this->comment = array_key_exists('COMMENT', $fields) ? $fields['COMMENT'] : $this->comment;
		$this->worktimeSkipped = array_key_exists('WORKTIME_SKIPPED', $fields) ? $fields['WORKTIME_SKIPPED'] == 'Y' : $this->worktimeSkipped;
		$this->sipHeaders = array_key_exists('SIP_HEADERS', $fields) ? $fields['SIP_HEADERS'] : $this->sipHeaders;
		$this->gatheredDigits = array_key_exists('GATHERED_DIGITS', $fields) ? $fields['GATHERED_DIGITS'] : $this->gatheredDigits;
		$this->parentCallId = array_key_exists('PARENT_CALL_ID', $fields) ? $fields['PARENT_CALL_ID'] : $this->parentCallId;
		$this->lastPingDate = array_key_exists('LAST_PING', $fields) ? $fields['LAST_PING'] : $this->lastPingDate;
		$this->executionGraph = array_key_exists('EXECUTION_GRAPH', $fields) && ($fields['EXECUTION_GRAPH'] instanceof Node) ? $fields['EXECUTION_GRAPH'] : $this->executionGraph;

		if($fields['CONFIG_ID'] && !$this->config)
		{
			$this->config = \CVoxImplantConfig::GetConfig($fields['CONFIG_ID']);
		}
	}
}