Программное создание документов и наполнение их данными из различных источников — это базовая задача любого разработчика 1С. Однако на этом пути часто возникают трудности, связанные с непониманием типов данных, механизмов работы выборок и оптимизации запросов к базе данных. В этой статье мы подробно разберем, как правильно реализовать алгоритм заполнения документа, проанализируем типичные ошибки и рассмотрим способы улучшения производительности кода.
Одной из самых частых ошибок, с которой столкнулся автор темы, является попытка присвоить реквизиту документа объект типа СправочникВыборка. Давайте проанализируем ситуацию: когда мы используем метод Выбрать(), система возвращает нам динамический указатель на набор записей. При обходе этого набора в цикле Пока Выборка.Следующий(), сама переменная Выборка не становится ссылкой на конкретный элемент. Она остается "курсором", который указывает на текущую строку.
Важный момент: реквизиты документа и табличных частей имеют строго определенный тип, например, СправочникСсылка.Подразделения. Если попытаться записать в такое поле объект выборки, система не сможет выполнить неявное преобразование и в итоге запишет в базу Пустую ссылку. Чтобы получить корректное значение, необходимо обращаться к свойству .Ссылка текущей записи выборки.
Рассмотрим правильный фрагмент кода для обхода выборки:
Пока ВыборкаПодразделения.Следующий() Цикл
НоваяСтрока = ДокНов.Сценарии.Добавить();
// ОШИБКА: НоваяСтрока.ПодразделениеТаб = ВыборкаПодразделения;
// ПРАВИЛЬНО:
НоваяСтрока.ПодразделениеТаб = ВыборкаПодразделения.Ссылка;
КонецЦикла;
В процессе разработки часто требуется найти элемент справочника по какому-либо признаку, например, по коду. Разберем ошибку, допущенную в исходном сообщении, где поиск сценария ошибочно производился в справочнике подразделений. Это критическая логическая ошибка: метод НайтиПоКоду вернет пустую ссылку, если вы ищете код "001" из справочника "Сценарии" внутри справочника "Подразделения".
Кроме того, обратим внимание на использование функции Формат. При поиске по коду крайне важно учитывать длину кода и наличие префиксов (нулей). Если код в метаданных определен как строка длиной 9 символов с автонумерацией, то поиск по числу "1" может не дать результата, так как в базе код хранится как "000000001".
Проанализируем правильный подход к поиску:
// Формируем строку кода с учетом ведущих нулей (если это предусмотрено длиной кода)
КодДляПоиска = Формат(НомерСтроки, "ЧЦ=9; ЧРГ=; ЧВН=; ЧГ=0");
переменСценарии = Справочники.Сценарии.НайтиПоКоду(КодДляПоиска);
Если Не переменСценарии.Пустая() Тогда
НоваяСтрока.Сценарии = переменСценарии;
КонецЕсли;
Использование метода НайтиПоКоду внутри цикла — это верный способ замедлить работу системы. Рассмотрим причину: если в вашем справочнике 1000 элементов, и вы вызываете поиск в цикле 1000 раз, программа сделает 1000 отдельных обращений к СУБД. Это классическая проблема производительности (N+1).
Вместо этого мы рекомендуем использовать язык запросов 1С. Выясним, как это сделать эффективнее. Мы можем одним запросом получить все необходимые данные из обоих справочников и выгрузить их в таблицу значений. Это сократит количество обращений к базе данных до одного.
Рассмотрим пример оптимизированного подхода с использованием запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Подразделения.Ссылка КАК Подразделение
|ИЗ
| Справочник.Подразделения КАК Подразделения";
РезультатПодразделения = Запрос.Выполнить().Выгрузить();
// Теперь мы можем работать с таблицей значений в памяти, что значительно быстрее
Для Каждого СтрокаДанных Из РезультатПодразделения Цикл
НоваяСтрока = ДокНов.Сценарии.Добавить();
НоваяСтрока.ПодразделениеТаб = СтрокаДанных.Подразделение;
КонецЦикла;
Если данных очень много, даже метод Добавить() в цикле может работать медленно. В 1С существует мощный инструмент — метод Загрузить(). Он позволяет мгновенно заполнить всю табличную часть документа из подготовленной Таблицы Значений. Главное условие — имена колонок в вашей таблице должны полностью совпадать с именами реквизитов табличной части документа.
Разберем этот метод по шагам:
ТаблицуЗначений.ДокНов.Сценарии.Загрузить(НашаТаблица).Этот способ не только ускоряет выполнение кода, но и делает его более читаемым и структурированным.
После того как все реквизиты шапки и строки табличной части заполнены, документ необходимо физически сохранить в базе данных. Для этого используется метод Записать().
Проанализируем возможные режимы записи:
ДокНов.Записать(РежимЗаписиДокумента.Запись); — документ будет просто сохранен как черновик (не проведен).ДокНов.Записать(РежимЗаписиДокумента.Проведение); — документ будет записан и проведен, то есть сделает движения по регистрам (если это предусмотрено логикой конфигурации).Важно: Всегда оборачивайте процесс создания и записи документа в конструкцию Попытка ... Исключение. Это позволит избежать аварийного завершения программы, если, например, документ заблокирован другим пользователем или не заполнены обязательные реквизиты, установленные на уровне метаданных.
Попытка
ДокНов.Записать(РежимЗаписиДокумента.Запись);
Сообщить("Документ успешно создан: " + ДокНов.Ссылка);
Исключение
Сообщить("Не удалось записать документ! Ошибка: " + ОписаниеОшибки());
КонецПопытки;
Подводя итог, при программном создании документов в 1С всегда придерживайтесь следующих правил:
СправочникОбъект, СправочникСсылка и СправочникВыборка — это принципиально разные вещи.НайтиПоНаименованию, НайтиПоКоду) внутри циклов. Используйте запросы.Загрузить().Записать().Соблюдение этих принципов позволит вам писать качественный, производительный и отказоустойчивый код, который будет легко поддерживать в будущем.