Битрикс

Отслеживание изменений пользовательских полей в Битрикс24

Штатно из коробки Битрикс не умеет добавлять в историю изменения пользовательских полей в CRM элементах. Но мы это исправим.

Для начала следует заменить фабрику через ServiceLocator. В моем примере я работаю со смарт-процессом с идентификатором 171. В вашем случае, это может быть deal, lead, contact и пр.

Loader::includeModule('crm');

$type = Service\Container::getInstance()->getTypeByEntityTypeId(171);
$factory = new class($type) extends Service\Factory\Dynamic {
// ... тут исправим новые методы
}
\Bitrix\Main\DI\ServiceLocator::getInstance()->addInstance('crm.service.factory.dynamic.171', $factory);

Для начала исправим метод получения полей для отслеживания изменений, добавив туда ключи пользовательских полей

// заменяем метод фабрики getTrackedFieldNames и подсовывем на выходе массив с uf полями
protected function getTrackedFieldNames(): array
{
    $parent = parent::getTrackedFieldNames();
    $map = $this->getUserFields();
    return array_merge($parent, array_keys($map));
}

Ура! отслеживание включено и записывается в историю изменений.
Но, появилась проблема вывода имени поля вместо ключа.

Таблица с историй изменений где красным подчеркнуты неверные имена полей

На этот случай заменим метод getFieldCaption


// выводим правильные title через подмену getFieldCaption
    public function getFieldCaption(string $commonFieldName): string
    {
        $fieldCaption = parent::getFieldCaption($commonFieldName);
        if($fieldCaption == $commonFieldName) {
            $map = $this->getUserFields();
            if(key_exists($commonFieldName,$map)) {
                return $map[$commonFieldName]['EDIT_FORM_LABEL'];
            }
        }
        return $fieldCaption;
    }

И в добавок исправим ошибки вывода array в множественных полях и имени файла вместо идентификатора


// Исправляем ошибку с полями в типа "файл" и массивы подменой метода getFieldValueCaption
    public function getFieldValueCaption($commonFieldName, $fieldValue): string
    {
        $field = $this->getFieldsCollection()->getField($commonFieldName);

        if ($field->getType() === \Bitrix\Crm\Field::TYPE_FILE) {
            if (is_array($fieldValue)) {
                $arFileName = [];
                foreach ($fieldValue as $fileId) {
                   $fileArray = \CFile::GetFileArray($fileId);
                    $arFileName[] = $fileArray['FILE_NAME'];
                }
                $fieldValue = implode(',',$arFileName);
            } else {
                $fieldValue = \CFile::GetFileArray($fieldValue)['FILE_NAME'];
            }
            return $fieldValue ? (string)$fieldValue : Loc::getMessage('CRM_COMMON_EMPTY');
        } else {
            return parent::getFieldValueCaption($commonFieldName, $fieldValue);
        }
    }

Итоговый результат. Можно добавить в init.php


Loader::includeModule('crm');

$type = Service\Container::getInstance()->getTypeByEntityTypeId(171);
$factory = new class($type) extends Service\Factory\Dynamic {

    // заменим метод фабрики getTrackedFieldNames и подмешаем на выходе массив с uf полями
    protected function getTrackedFieldNames(): array
    {
        $parent = parent::getTrackedFieldNames();
        $map = $this->getUserFields();
        return array_merge($parent, array_keys($map));
    }

    // выводим правильные title через подмену getFieldCaption
    public function getFieldCaption(string $commonFieldName): string
    {
        $fieldCaption = parent::getFieldCaption($commonFieldName);
        if($fieldCaption == $commonFieldName) {
            $map = $this->getUserFields();
            if(key_exists($commonFieldName,$map)) {
                return $map[$commonFieldName]['EDIT_FORM_LABEL'];
            }
        }
        return $fieldCaption;
    }

    // Исправляем ошибку с полями в типа "файл" и массивы подменой метода getFieldValueCaption
    public function getFieldValueCaption($commonFieldName, $fieldValue): string
    {
        $field = $this->getFieldsCollection()->getField($commonFieldName);

        if ($field->getType() === \Bitrix\Crm\Field::TYPE_FILE) {
            if (is_array($fieldValue)) {
                $arFileName = [];
                foreach ($fieldValue as $fileId) {
                   $fileArray = \CFile::GetFileArray($fileId);
                    $arFileName[] = $fileArray['FILE_NAME'];
                }
                $fieldValue = implode(',',$arFileName);
            } else {
                $fieldValue = \CFile::GetFileArray($fieldValue)['FILE_NAME'];
            }
            return $fieldValue ? (string)$fieldValue : Loc::getMessage('CRM_COMMON_EMPTY');
        } else {
            return parent::getFieldValueCaption($commonFieldName, $fieldValue);
        }
    }

};


DI\ServiceLocator::getInstance()->addInstance('crm.service.factory.dynamic.171', $factory);

Возврат к списку