Можно ли обойтись без регистратора в регистре накопления 1С и как правильно спроектировать базу данных для звонков?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции 1С с IP-телефонией (например, Asterisk, 3CX, Гравител и др.) перед разработчиком часто встает задача: где и как хранить историю звонков? С одной стороны, звонки генерируют огромный поток транзакционной информации — даты, направления, длительность, ответственные менеджеры. С другой стороны, по этим данным необходимо оперативно строить отчеты: рассчитывать общую продолжительность разговоров, количество успешных и пропущенных вызовов за период.

Первое архитектурное решение, которое приходит в голову начинающему разработчику — использовать оборотный Регистр накопления (РН). Ведь именно этот объект конфигурации идеально приспособлен для быстрого получения оборотных показателей. Однако при попытке программной записи данных мы сталкиваемся с жестким ограничением платформы: регистр накопления невозможно использовать без документа-регистратора.

В этой статье мы подробно разберем, почему платформа накладывает такое ограничение, рассмотрим альтернативные варианты проектирования и выберем наиболее оптимальные подходы в зависимости от объемов вашей базы данных.

Почему регистр накопления физически не может существовать без регистратора?

Для того чтобы понять архитектурный замысел разработчиков платформы «1С:Предприятие», заглянем на уровень физических таблиц СУБД. На уровне базы данных любая таблица движений регистра накопления (как оборотного, так и остатков) содержит обязательное поле Recorder (Регистратор). Это поле входит в состав кластеризованного уникального индекса СУБД.

В концепции 1С регистр накопления не является самостоятельной сущностью. Он предназначен для отражения в учете событий, которые инициируются документами. Документ в данном случае олицетворяет собой транзакцию бизнес-логики. Запись в регистр без ссылки на существующий документ нарушила бы целостность ссылочной структуры базы данных и сделала бы невозможным корректный пересчет итогов при отмене проведения или удалении документов.

Таким образом, обойтись без регистратора в регистре накопления невозможно технически. Но как быть, если данные поступают из внешнего источника (например, через HTTP-сервис или регламентное задание) и дублировать их в виде сотен тысяч документов в системе не хочется?

Рассмотрим детально рабочие альтернативные варианты решения этой задачи.

Вариант 1. Использование независимого регистра сведений

Если ваша задача — просто сохранять параметры звонков и строить по ним несложные отчеты, лучшим решением будет использование независимого регистра сведений (РС). В отличие от регистра накопления, независимый регистр сведений не привязан к документам. Мы можем писать в него напрямую с помощью менеджера записи или набора записей.

Рассмотрим пример кода для записи входящего звонка в независимый регистр сведений ИсторияЗвонков:


// Создаем менеджер записи для добавления одной строки
НоваяЗапись = РегистрыСведений.ИсторияЗвонков.СоздатьМенеджерЗаписи();

// Заполняем ключевые измерения
НоваяЗапись.Период = ТекущаяДата();
НоваяЗапись.УникальныйИдентификаторЗвонка = ПараметрыЗвонка.UID; // уникальный ID из телефонии

// Заполняем ресурсы и реквизиты
НоваяЗапись.Абонент = ПараметрыЗвонка.Абонент;
НоваяЗапись.Длительность = ПараметрыЗвонка.Длительность;
НоваяЗапись.Направление = ПараметрыЗвонка.Направление;
НоваяЗапись.Менеджер = ПараметрыЗвонка.Менеджер;

// Записываем данные напрямую в базу без участия документов
НоваяЗапись.Записать(Истина);

Преимущества использования регистра сведений:

  1. Нет избыточных объектов в базе данных (не нужно создавать документы-пустышки).
  2. Простая и быстрая запись напрямую из внешних API.
  3. Возможность легко хранить детальную информацию (например, ссылки на файлы записей разговоров, текстовые комментарии и детальные статусы).

Минусы регистра сведений:

Совет по оптимизации регистра сведений: Если вы используете платформу версии 8.3.1 и выше, для наиболее часто используемых в отборах ресурсов (например, Длительность) вы можете установить свойство «Индексировать». Это несколько повысит скорость работы отборов, хотя и не заменит полноценные таблицы итогов регистра накопления.

Вариант 2. Использование регистра накопления с периодическим техническим регистратором

Что делать, если объемы звонков огромны (сотни тысяч или миллионы записей в месяц), а менеджеры хотят видеть мгновенные отчетики по KPI прямо на рабочем столе? В этом случае использование таблиц итогов регистра накопления становится критически важным.

Чтобы решить проблему с обязательным наличием регистратора, мы можем применить паттерн «Технический регистратор». Суть метода заключается в том, что мы создаем специальный документ (например, ЗагрузкаЗвонков), который не несет смысловой бизнес-нагрузки, а служит лишь «контейнером» для проведения движений по регистру накопления за определенный период.

Важное предостережение: Ни в коем случае не используйте один и тот же единственный документ («документ-пустышку») в качестве регистратора для всех записей за все время! Запись миллионов строк движений под один регистратор гарантированно приведет к критическим блокировкам базы данных при параллельной загрузке, а попытка открыть такой документ в интерфейсе или прочитать его движения намертво повесит сеанс работы пользователя.

Правильный подход — создавать по одному техническому документу на каждую пачку загрузки либо с периодичностью один раз в день. Проанализируем пример кода, реализующий такую схему:


// Функция ищет существующий технический документ за указанную дату или создает новый
Функция ПолучитьТехническийРегистратор(ДатаЗагрузки) Экспорт
    
    НачалоДня = НачалоДня(ДатаЗагрузки);
    КонецДня = КонецДня(ДатаЗагрузки);
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	ЗагрузкаЗвонков.Ссылка КАК Ссылка
        |ИЗ
        |	Документ.ЗагрузкаЗвонков КАК ЗагрузкаЗвонков
        |ГДЕ
        |	ЗагрузкаЗвонков.Дата МЕЖДУ &НачалоДня И &КонецДня";
        
    Запрос.УстановитьПараметр("НачалоДня", НачалоДня);
    Запрос.УстановитьПараметр("КонецДня", КонецДня);
    
    РезультатЗапроса = Запрос.Выполнить();
    
    Если Не РезультатЗапроса.Пустой() Тогда
        Выборка = РезультатЗапроса.Выбрать();
        Выборка.Следующий();
        Возврат Выборка.Ссылка;
    Иначе
        // Создаем новый технический документ для группировки дневных движений
        НовыйДокумент = Документы.ЗагрузкаЗвонков.СоздатьДокумент();
        НовыйДокумент.Дата = КонецДня;
        НовыйДокумент.Комментарий = "Создан автоматически для хранения движений звонков за " + Формат(ДатаЗагрузки, "ДФ=dd.MM.yyyy");
        НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);
        Возврат НовыйДокумент.Ссылка;
    КонецЕсли;
    
КонецФункции

// Процедура пакетной записи звонков в Регистр Накопления
Процедура ЗаписатьЗвонкиВРегистрНакопления(ТаблицаДанныхЗвонков, ДатаЗагрузки) Экспорт
    
    РегистраторСсылка = ПолучитьТехническийРегистратор(ДатаЗагрузки);
    
    // Создаем набор записей регистра накопления с отбором по нашему техническому документу
    НаборДвижений = РегистрыНакопления.ЗвонкиОбороты.СоздатьНаборЗаписей();
    НаборДвижений.Отбор.Регистратор.Установить(РегистраторСсылка);
    
    // Считываем существующие движения, чтобы не затереть их при добавлении новых в течение дня
    НаборДвижений.Прочитать();
    
    Для Каждого СтрокаЗвонка Из ТаблицаДанныхЗвонков Цикл
        Движение = НаборДвижений.Добавить();
        Движение.Период = СтрокаЗвонка.ДатаВремя;
        Движение.Регистратор = РегистраторСсылка;
        
        // Заполняем измерения
        Движение.Направление = СтрокаЗвонка.Направление;
        Движение.Менеджер = СтрокаЗвонка.Менеджер;
        Движение.Абонент = СтрокаЗвонка.Абонент;
        
        // Заполняем ресурсы
        Движение.Количество = 1;
        Движение.Длительность = СтрокаЗвонка.Продолжительность;
    КонецЦикла;
    
    // Записываем набор записей в базу данных
    НаборДвижений.Записать();
    
КонецПроцедуры

Используя данный подход, мы получаем все преимущества оборотного регистра накопления (быстрые отсчеты по таблицам итогов) и при этом не захламляем базу данных миллионами мелких документов-регистраторов, распределяя нагрузку по дням.

Вариант 3. Использование механизма агрегатов для сверхбольших объемов данных

Если количество звонков в вашей организации измеряется миллионами записей в неделю, даже классический оборотный регистр накопления начнет снижать производительность при расчете итогов. В этом случае на помощь приходит встроенный механизм агрегатов, разработанный в 1С специально для оптимизации оборотных регистров накопления.

Агрегаты позволяют хранить данные в «свернутом» (агрегированном) виде по заданным разработчиком измерениям. Например, вы можете настроить систему так, чтобы она автоматически хранила предварительно свернутые данные по дням в разрезе только измерений Менеджер и Направление, игнорируя детальное измерение Абонент.

Когда пользователь запустит отчет по эффективности менеджеров, система не будет обращаться к основной таблице движений и даже к стандартной таблице итогов. Она мгновенно возьмет данные из готовой таблицы агрегатов, что ускорит выполнение аналитического запроса в десятки и сотни раз.

Вариант 4. Альтернатива: Внешние источники данных

Рассмотрим современную альтернативную архитектуру. Очень часто детальная информация о звонках (CDR — Call Detail Records) нужна в 1С исключительно для построения аналитических отчетов и не участвует непосредственно во внутренних транзакциях системы (таких как проведение заказов, начисление зарплаты и т.д.).

Зачем в таком случае вообще нагружать транзакционную базу 1С (файл .1CD или таблицы MS SQL/PostgreSQL) миллионами тяжелых строк? Оптимальным решением будет оставить эти данные в "родной" базе данных IP-телефонии и подключить ее к 1С через механизм Внешних источников данных.

Платформа 1С позволяет напрямую связываться со сторонними базами данных (через ODBC-драйверы) и работать с внешними таблицами в привычном стиле запросов 1С. Вы сможете строить отчеты на СКД, соединяя данные ваших документов 1С с внешней таблицей звонков налету, не совершая физический импорт миллионов строк в основную базу.

Резюме и матрица принятия решений

Подведем итог. Выбор оптимального архитектурного решения зависит от планируемого объема данных в вашей системе:

Объем звонков в месяц Рекомендуемое решение Почему именно оно?
До 100 000 записей Независимый регистр сведений (РС) Простота реализации, чистота метаданных, отсутствие лишних технических объектов в конфигурации. Быстродействие СУБД будет достаточным без использования итогов.
От 100 000 до 1 000 000 записей Регистр накопления (РН) + Технический регистратор (период — день) Использование преимуществ таблиц итогов для мгновенного построения аналитических отчетов без перегрузки СУБД полным сканированием таблиц.
Более 1 000 000 записей РН с включенными агрегатами ИЛИ Внешние источники данных Обеспечение максимальной производительности. Исключение риска раздувания транзакционной базы 1С до критических объемов.

Проанализировав требования вашего бизнеса к отчетности и спрогнозировав объемы входящих данных, вы сможете спроектировать надежную, быструю и отказоустойчивую систему интеграции телефонии с вашей конфигурацией 1С.

← На главную