Как правильно отразить приход только товаров по регистру, если в документе есть и услуги

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

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

Метод 1. Простой обход табличной части в цикле

Начнем с самого очевидного способа, который часто выбирают начинающие разработчики. Он заключается в переборе строк табличной части непосредственно в процедуре ОбработкаПроведения. Разберем этот пример кода, чтобы понять его механику и возможные «подводные камни».


Процедура ОбработкаПроведения(Отказ, Режим)
    Движения.ОстаткиТоваров.Записывать = Истина;
    
    // Обходим строки документа
    Для Каждого СтрокаТЧ Из Товары Цикл
        // Проверяем, что тип номенклатуры - "Товар"
        Если СтрокаТЧ.Номенклатура.ТипНоменклатуры = Перечисления.ТипНоменклатуры.Товар Тогда
            // Формируем запись в регистр ОстаткиТоваров
            Движение = Движения.ОстаткиТоваров.Добавить();
            Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
            Движение.Период = Дата;
            Движение.Склад = Склад;
            Движение.Номенклатура = СтрокаТЧ.Номенклатура;
            Движение.Количество = СтрокаТЧ.Количество;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Проанализируем ситуацию: на первый взгляд код выглядит простым и понятным. Однако здесь скрыта серьезная проблема производительности. Обращение СтрокаТЧ.Номенклатура.ТипНоменклатуры является «разыменованием» ссылки. Это означает, что на каждой итерации цикла, где встречается товар или услуга, система может выполнять неявный запрос к базе данных, чтобы получить значение реквизита из справочника Номенклатура. Если в документе сотни строк, это приведет к существенному замедлению проведения. Мы называем это «проблемой N+1 запрос».

Метод 2. Использование запроса для фильтрации (Рекомендуемый)

Для оптимизации работы системы и исключения лишних обращений к БД в цикле, мы рассмотрим использование объекта Запрос. Это позволяет нам одним махом получить все необходимые данные, уже отфильтрованные по нужному критерию. Выясним причину, почему это быстрее: запрос выполняется на стороне сервера базы данных, и в память приложения попадают только те строки, которые действительно должны сформировать движения.

Рассмотрим по шагам реализацию этого метода:

  1. Подготовим текст запроса, который выбирает данные из табличной части текущего документа.
  2. Установим фильтр по условию ТипНоменклатуры = Значение(Перечисление.ТипНоменклатуры.Товар).
  3. Выгрузим результат во временную таблицу или сразу в итоговую выборку.

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ДокТовары.Номенклатура КАК Номенклатура,
    |	ДокТовары.Количество КАК Количество,
    |	&Период КАК Период,
    |	&Склад КАК Склад
    |ИЗ
    |	Документ.ПриходТоваров.Товары КАК ДокТовары
    |ГДЕ
    |	ДокТовары.Ссылка = &Ссылка
    |	И ДокТовары.Номенклатура.ТипНоменклатуры = Значение(Перечисление.ТипНоменклатуры.Товар)";

Запрос.УстановитьПараметр("Ссылка", Ссылка);
Запрос.УстановитьПараметр("Период", Дата);
Запрос.УстановитьПараметр("Склад", Склад);

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

Пока Выборка.Следующий() Цикл
    Движение = Движения.ОстаткиТоваров.Добавить();
    ЗаполнитьЗначенияСвойств(Движение, Выборка);
    Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
КонецЦикла;

Этот подход значительно эффективнее. Мы не только фильтруем услуги на уровне базы данных, но и используем метод ЗаполнитьЗначенияСвойств, что делает код чище и устойчивее к изменениям структуры регистра.

Метод 3. Профессиональная оптимизация через «Загрузить»

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

Разберем этот пример подробнее:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	&Период КАК Период,
    |	ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход) КАК ВидДвижения,
    |	&Склад КАК Склад,
    |	Док.Номенклатура КАК Номенклатура,
    |	Док.Количество КАК Количество
    |ИЗ
    |	Документ.ПриходТоваров.Товары КАК Док
    |ГДЕ
    |	Док.Ссылка = &Ссылка
    |	И Док.Номенклатура.ТипНоменклатуры = Значение(Перечисление.ТипНоменклатуры.Товар)";

Запрос.УстановитьПараметр("Ссылка", Ссылка);
Запрос.УстановитьПараметр("Период", Дата);
Запрос.УстановитьПараметр("Склад", Склад);

ТаблицаДвижений = Запрос.Выполнить().Выгрузить();

Движения.ОстаткиТоваров.Записывать = Истина;
Движения.ОстаткиТоваров.Загрузить(ТаблицаДвижений);

Важный момент: при использовании метода Загрузить, система заменяет текущие записи в наборе новыми данными из таблицы. Это самый быстрый способ формирования движений. Однако помните, что имена полей в Запросе (через КАК) должны строго соответствовать именам в регистре ОстаткиТоваров, включая регистр букв.

Дополнительные рекомендации по качеству кода

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

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

← На главную