Как правильно проверить наличие движений документа в 1С:Предприятие 8?

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

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

Анализ проблемного кода: почему Количество() возвращает ноль?

Рассмотрим ситуацию, с которой часто сталкиваются начинающие разработчики (неофиты), игнорирующие общепринятые стандарты и методики разработки конфигураций 1С. Предположим, у нас есть ссылка на документ ссылкаДокументРеализации, и нам нужно узнать, есть ли у него движения в регистре накопления "Продажи". Типичный ошибочный код выглядит так:


Если ссылкаДокументРеализации.ПолучитьОбъект().Движения.Продажи.Количество() > 0 Тогда
    // Какая-то логика
КонецЕсли;

Проанализируем этот фрагмент. Программист ожидает, что раз документ проведен и в базе записи есть, то метод Количество() их вернет. Но на практике этот код всегда будет выдавать 0 (если только вы не вызвали его непосредственно в модуле объекта сразу после записи). Почему так происходит? Разберем по шагам:

  1. Метод ПолучитьОбъект() считывает данные документа из базы данных.
  2. В коллекцию Движения при этом загружаются только "пустые оболочки" наборов записей.
  3. Сами записи регистров (строки в таблицах БД) автоматически не считываются. Это сделано для экономии ресурсов: движений могут быть тысячи, и загружать их каждый раз при открытии объекта документа было бы слишком накладно.

Для того чтобы коллекция заполнилась данными, необходимо явно вызвать метод Прочитать(). Рассмотрим исправленный (но все еще не оптимальный) вариант:


ОбъектДок = ссылкаДокументРеализации.ПолучитьОбъект();
НаборДвижений = ОбъектДок.Движения.Продажи;
НаборДвижений.Прочитать(); // Вот оно, "волшебство"

Если НаборДвижений.Количество() > 0 Тогда
    // Теперь это сработает
КонецЕсли;

Смертельная опасность: как проверка движений может их удалить

Проанализируем ситуацию с точки зрения безопасности данных. Существует огромный риск при использовании объектной техники для простых проверок. Представьте, что после выполнения кода выше, в этом же контексте, вызывается ОбъектДок.Записать(). Если метод Прочитать() не был вызван, набор записей остается пустым. При записи объекта система посмотрит на этот пустой набор и, если свойство Записывать установлено в Истина, просто удалит все существующие движения документа из базы данных.

Именно поэтому использование ПолучитьОбъект() для простых проверок считается плохим тоном и "говнокодом". Мы не только рискуем данными, но и совершаем избыточные действия.

Производительность: ПолучитьОбъект() против Запроса

Теперь выясним причину, по которой опытные разработчики избегают обращения к объекту там, где можно обойтись ссылкой. Вызов ПолучитьОбъект() — это крайне тяжелая операция для СУБД. Чтобы понять масштаб проблемы, рекомендуется провести замер производительности контура 1С и поиск узких мест — для этого подойдёт инструмент анализа производительности 1С и планов запросов СУБД. Проанализируем, что происходит "под капотом":

Если нам нужно просто проверить наличие хотя бы одной записи в регистре, зачем нам "тащить" из базы сотни строк табличной части "Товары"? Это классический пример нерационального использования ресурсов.

Решение проблемы с помощью запроса (Best Practice)

Рассмотрим самый эффективный и безопасный способ решения задачи. Для проверки наличия движений следует использовать запрос к физической таблице регистра. Нам не нужно знать количество записей, нам достаточно факта наличия хотя бы одной. Поэтому мы используем конструкцию ВЫБРАТЬ ПЕРВЫЕ 1, соблюдая рекомендации, которые дает проверка запросов на лишнюю выборку.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |	1 КАК Поле1
    |ИЗ
    |	РегистрНакопления.Продажи КАК Продажи
    |ГДЕ
    |	Продажи.Регистратор = &Регистратор";

Запрос.УстановитьПараметр("Регистратор", ссылкаДокументРеализации);

РезультатЗапроса = Запрос.Выполнить();

Если НЕ РезультатЗапроса.Пустой() Тогда
    // Движения есть!
КонецЕсли;

Почему это лучше?

  1. Минимум данных: СУБД передает только одно число (единицу), если запись найдена.
  2. Индексы: Поле Регистратор во всех физических таблицах регистров индексировано. Поиск по нему происходит мгновенно.
  3. Никаких блокировок: Обычный запрос не накладывает блокировок на объект, в отличие от ПолучитьОбъект() в определенных режимах.

Использование функций БСП

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


// Проверка наличия движений вообще по всем регистрам, где документ является регистратором
ЕстьХотьКакиеТоДвижения = ОбщегоНазначения.ЕстьДвиженияПоРегистрам(ссылкаДокументРеализации);

Эта функция внутри себя также использует оптимизированные запросы, что избавляет разработчика от необходимости писать велосипеды и гарантирует высокую скорость работы.

Разбор нюансов с "битыми" ссылками

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

Именно поэтому проверка через ЗначениеЗаполнено() для ссылок не всегда достаточна — она лишь проверяет, что GUID ссылки не пустой, но не гарантирует наличие записи в таблице БД. Для решения этой задачи есть инструмент диагностики регистров на наличие битых ссылок.

Резюме

Подводя итог нашему разбору, сформулируем правила "хорошего тона" для разработчика 1С:

← На главную