В практике разработки на платформе 1С:Предприятие часто возникает задача программного заполнения табличных частей. Типичный сценарий: мы перебираем некий массив данных (например, загруженных из внешнего файла), ищем соответствующие элементы в справочниках и добавляем их в документ или обработку. Однако сложности начинаются тогда, когда объект в базе не найден. В этой статье мы подробно разберем, почему может казаться, что строки не добавляются, как правильно работать с пустыми ссылками и как избежать ошибок при передаче этих данных в запросы.
Рассмотрим стандартный механизм добавления строки. Метод Добавить() коллекции строк табличной части работает безусловно. Это значит, что как только управление проходит через эту строку кода, в памяти создается новый объект строки, независимо от того, какие значения будут присвоены ее реквизитам далее.
Проанализируем фрагмент кода, с которым возникли сложности у автора темы:
НоваяСтрока = Объект.ДанныеТО.Добавить;
НоваяСтрока.Контрагент = ДанныеСтроки[0]; // Ошибка: попытка присвоить строку ссылочному типу
НайтиКонтрагента = Справочники.Контрагенты.НайтиПоНаименованию(ДанныеСтроки[0]);
НоваяСтрока.Контрагент = НайтиКонтрагента;
Разберем, что здесь происходит. Сначала вызывается метод Добавить(). В этот момент в табличной части ДанныеТО уже появилась новая строка. Даже если поиск НайтиПоНаименованию вернет пустую ссылку, строка никуда не исчезнет — она просто будет содержать пустое значение в колонке Контрагент.
Выясним причину, по которой поиск может работать не так, как ожидается. Метод НайтиПоНаименованию имеет важный нюанс. Если не указать второй параметр (Ложь по умолчанию), 1С ищет по частичному совпадению с начала строки. Кроме того, если передать в метод пустую строку, система может вернуть не пустую ссылку, а первый попавшийся элемент справочника. Чтобы поиск был предсказуемым, мы рекомендуем всегда использовать второй параметр:
// Ищем точное соответствие наименования
НайтиКонтрагента = Справочники.Контрагенты.НайтиПоНаименованию(ИскомоеИмя, Истина);
Если НайтиКонтрагента.Пустая() Тогда
// Обработка ситуации, когда контрагент не найден
Сообщить("Контрагент " + ИскомоеИмя + " не обнаружен!");
КонецЕсли;
Важно помнить: присвоение Справочники.Контрагенты.ПустаяСсылка реквизиту — это нормальная операция. Строка все равно будет добавлена, просто в интерфейсе в этой ячейке пользователь ничего не увидит.
Как выяснилось в обсуждении, проблема часто кроется не в самом добавлении строки, а в последующей обработке этой таблицы в запросе. Когда мы передаем табличную часть в запрос в качестве параметра, система должна четко понимать типы данных в колонках.
Если ваша таблица содержит пустые ссылки, при построении временной таблицы в запросе тип колонки может определиться неверно или стать составным. Рассмотрим, как правильно подготовить данные для запроса, чтобы не потерять "пустые" строки. Если вы используете временную таблицу, полезно явно типизировать колонки при создании таблицы значений (если данные берутся из нее):
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("СправочникСсылка.Контрагенты"));
ОписаниеТипаКонтрагент = Новый ОписаниеТипов(МассивТипов);
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Контрагент", ОписаниеТипаКонтрагент);
В самом тексте запроса, если вам нужно объединить данные или создать выборку, содержащую пустые ссылки определенного типа, используйте конструкцию ЗНАЧЕНИЕ:
ВЫБРАТЬ
ТЧ.Контрагент КАК Контрагент,
ТЧ.Сумма КАК Сумма
ИЗ
&ДанныеТаблицы КАК ТЧ
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка),
0
Разберем по шагам разницу между этими понятиями, так как в сообщениях форума предлагалось использовать NULL. Это критическая точка для понимания программистом 1С:
ЕСТЬNULL(Поле, ...) не сработает для пустой ссылки, так как значение физически существует.ЛЕВОМ СОЕДИНЕНИИ, когда в правой таблице не нашлось подходящей записи.Если в вашем запросе строки с пустыми ссылками "исчезают", проверьте условия связи. Если вы делаете ВНУТРЕННЕЕ СОЕДИНЕНИЕ по полю Контрагент, и в табличной части у вас пустая ссылка, запрос будет искать в справочнике контрагентов запись с "пустым" ID. Поскольку такой записи (с полностью нулевым GUID) в справочнике обычно нет, строка будет отсечена из результата.
Решение: используйте ЛЕВОЕ СОЕДИНЕНИЕ, если хотите сохранить все строки из исходной таблицы, даже те, где контрагент не указан.
Проанализируем еще одну причину, упомянутую в конце обсуждения — отсутствие записи объекта. Если вы программно добавляете строки в табличную часть объекта (например, документа) на сервере, эти изменения живут только в оперативной памяти до тех пор, пока не будет вызван метод Объект.Записать().
Рассмотрим правильный цикл обработки:
Для Каждого СтрокаДанных Из МассивДанных Цикл
НоваяСтрока = Объект.ДанныеТО.Добавить();
НайденнаяСсылка = Справочники.Контрагенты.НайтиПоНаименованию(СтрокаДанных.Имя, Истина);
// Даже если НайденнаяСсылка пустая, мы ее присваиваем
НоваяСтрока.Контрагент = НайденнаяСсылка;
НоваяСтрока.Количество = СтрокаДанных.Кол;
КонецЦикла;
// КРИТИЧЕСКИ ВАЖНО: сохраняем изменения в базе данных
Попытка
Объект.Записать(РежимЗаписиДокумента.Запись);
Исключение
Сообщить("Не удалось сохранить данные: " + КраткоеОписаниеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
Чтобы код был более универсальным и чистым, рекомендуем использовать функцию ЗначениеЗаполнено(). Она корректно проверяет ссылки (пустая или нет), строки, числа и значения NULL.
Посмотрим на пример улучшения логики:
Если НЕ ЗначениеЗаполнено(НоваяСтрока.Контрагент) Тогда
// Здесь мы точно знаем, что ссылка пустая, NULL или Неопределено
Сообщить("Внимание: добавлена строка без контрагента!");
КонецЕсли;
Подведем итог: добавление строки методом Добавить() происходит всегда. Если вы не видите строки, проверьте:
Записать() для объекта.ВНУТРЕННЕЕ СОЕДИНЕНИЕ, которое отсекает пустые ссылки.Использование этих простых правил позволит вам избежать большинства ошибок при работе с динамическими данными в 1С.