При разработке алгоритмов заполнения документов в 1С часто возникает ситуация, когда одну исходную запись нужно преобразовать в несколько строк табличной части. Типичная ошибка начинающих разработчиков заключается в неправильном использовании логических конструкций, что приводит к пропуску данных. Разберем подробно, как реализовать этот механизм правильно, оптимизировать производительность и сделать код надежным.
Рассмотрим ситуацию, описанную автором темы. В коде проверяются несколько полей (M1, M2, N1 и т.д.). Если значение в поле больше нуля, в документ должна добавиться строка с соответствующей номенклатурой. Автор использовал конструкцию Если ... ИначеЕсли ... КонецЕсли.
Проанализируем логику работы этого оператора: программа проверяет первое условие, и если оно истинно, выполняет блок кода и выходит из всей конструкции. Даже если условия для M2 или N3 тоже верны, система до них просто не дойдет. Выясним причину ошибки автора при попытке разделить условия: если оставить команду Добавить() вне блоков Если, то будет создана только одна строка, которая будет перезаписываться при каждом срабатывании условия, а в итоге в документе окажется результат только последней проверки.
Чтобы система проверила каждое условие независимо от того, сработало ли предыдущее, мы должны использовать последовательные блоки Если ... КонецЕсли. При этом создание новой строки Добавить() должно происходить строго внутри каждого блока. Посмотрим на правильную структуру кода:
// Правильный подход с независимыми проверками
Если стр.M1 > 0 Тогда
СтрКомисиионера = НовыйДокумент.Услуги.Добавить();
СтрКомисиионера.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("00-00000019");
СтрКомисиионера.Количество = стр.M1;
СтрКомисиионера.Цена = стр.Контрагент.M1;
КонецЕсли;
Если стр.M2 > 0 Тогда
СтрКомисиионера = НовыйДокумент.Услуги.Добавить();
СтрКомисиионера.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("00-00000023");
СтрКомисиионера.Количество = стр.M2;
СтрКомисиионера.Цена = стр.Контрагент.M2;
КонецЕсли;
Если стр.N3 > 0 Тогда
СтрКомисиионера = НовыйДокумент.Услуги.Добавить();
СтрКомисиионера.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("00-00000025");
СтрКомисиионера.Количество = стр.N3;
СтрКомисиионера.Цена = стр.Контрагент.N3;
КонецЕсли;
В данном примере, если у объекта заполнены и M1, и N3, в документ будут добавлены две разные строки. Это решает основную задачу автора.
Код выше содержит много дублирований (Copy-Paste). Согласно принципу Don't Repeat Yourself, повторяющиеся действия лучше вынести в отдельный алгоритм. Проанализируем, что меняется в каждом блоке: это только имя поля и код номенклатуры. Мы можем создать вспомогательную процедуру или использовать общую логику заполнения.
// Пример оптимизации заполнения
Процедура ДобавитьСтрокуЕслиНужно(ТабличнаяЧасть, ЗначениеПоля, ЦенаПоля, КодНоменклатуры)
Если ЗначениеПоля > 0 Тогда
НоваяСтрока = ТабличнаяЧасть.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоКоду(КодНоменклатуры);
НоваяСтрока.Количество = ЗначениеПоля;
НоваяСтрока.Цена = ЦенаПоля;
КонецЕсли;
КонецПроцедуры
// Вызов в основном цикле
ДобавитьСтрокуЕслиНужно(НовыйДокумент.Услуги, стр.M1, стр.Контрагент.M1, "00-00000019");
ДобавитьСтрокуЕслиНужно(НовыйДокумент.Услуги, стр.M2, стр.Контрагент.M2, "00-00000023");
Важный технический нюанс: использование НайтиПоКоду внутри цикла — это «антипаттерн». Каждый такой вызов провоцирует отдельный запрос к базе данных. Если строк в документе много, это критически замедлит работу системы. Профессиональный подход подразумевает предварительную подготовку данных.
Разберем по шагам, как оптимизировать поиск:
Соответствие (ключ — код, значение — ссылка).Соответствие, не обращаясь к базе.
// Оптимизированный поиск номенклатуры
Коды = Новый Массив;
Коды.Добавить("00-00000019");
Коды.Добавить("00-00000023");
// ... добавить остальные коды
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Ссылка, Код ИЗ Справочник.Номенклатура ГДЕ Код В (&Коды)";
Запрос.УстановитьПараметр("Коды", Коды);
Выборка = Запрос.Выполнить().Выбрать();
КэшНоменклатуры = Новый Соответствие;
Пока Выборка.Следующий() Цикл
КэшНоменклатуры.Вставить(Выборка.Код, Выборка.Ссылка);
КонецЦикла;
// Теперь в цикле используем:
НоменклатураСсылка = КэшНоменклатуры.Получить("00-00000019");
Если НоменклатураСсылка <> Неопределено И НЕ НоменклатураСсылка.Пустая() Тогда
// Заполняем строку...
КонецЕсли;
В ходе обсуждения на форуме возник важный вопрос: насколько надежно искать номенклатуру по коду? Код может изменить администратор или пользователь, и алгоритм «сломается». Рассмотрим альтернативы:
Справочники.Номенклатура.Доставка. Это самый надежный способ для системных элементов.Подводя итоги, отметим, что для корректного добавления нескольких строк необходимо использовать независимые условия и выполнять метод Добавить() строго внутри этих условий. Для обеспечения высокого качества кода следует избегать поиска в цикле, заменяя его кэшированием через запросы и Соответствие. Также рекомендуется уходить от жесткого кодирования строковых кодов в пользу настроек или предопределенных элементов, что сделает вашу разработку устойчивой к изменениям данных.