При разработке в 1С часто возникает необходимость автоматизировать заполнение документов на основе данных, подготовленных в специализированных обработках. Одна из таких задач – перенос данных из табличной части обработки в табличную часть нового или существующего документа. Давайте разберем этот процесс подробно, рассмотрим различные подходы и выясним, как избежать распространенных ошибок.
Предположим, у нас есть обработка с табличной частью под названием Товары, и мы хотим на ее основе создать документ НалоговаяНакладная, заполнив его табличную часть Товары. Для этого нам потребуется программный код, который будет выполняться, например, по нажатию кнопки на форме обработки.
Прежде всего, нам необходимо создать новый экземпляр документа, который мы собираемся заполнять, и установить для него основные реквизиты, такие как дата, организация, контрагент и договор. Это стандартная процедура при программном создании документов.
Рассмотрим пример кода:
// Создаем новый документ НалоговаяНакладная
Док = Документы.НалоговаяНакладная.СоздатьДокумент();
// Заполняем основные реквизиты шапки документа
Док.Дата = ТекущаяДата();
Док.Организация = Справочники.Организации.НайтиПоКоду("000000001"); // Пример поиска организации по коду
Док.Контрагент = Справочники.Контрагенты.НайтиПоКоду("000000001"); // Пример поиска контрагента по коду
Док.ДоговорКонтрагента = Справочники.ДоговорыКонтрагентов.НайтиПоКоду("000000001"); // Пример поиска договора
// ... другие необходимые реквизиты шапки документа
В этом блоке кода мы создаем объект документа и присваиваем значения его реквизитам. Обратите внимание, что мы используем методы НайтиПоКоду() для получения ссылок на элементы справочников. Это удобный способ, если коды элементов заранее известны.
Один из ключевых моментов – правильно получить доступ к данным исходной табличной части в обработке. Часто новички путают элемент формы, отображающий табличную часть, с самой табличной частью как объектом данных.
Если табличная часть Товары является реквизитом самой обработки (то есть, она добавлена в список реквизитов обработки как "Табличная часть"), то для доступа к ее данным мы будем использовать ЭтотОбъект.Товары или просто Товары, если код выполняется в контексте объекта обработки.
Важно: Не следует использовать ЭлементыФормы.Товары для доступа к данным. ЭлементыФормы.Товары – это визуальный элемент управления на форме, который лишь отображает данные. Сами данные хранятся в соответствующем реквизите объекта обработки или формы.
Таким образом, мы определим переменные для исходной и приемной табличных частей:
// Получаем ссылку на исходную табличную часть обработки
// Предполагается, что 'Товары' - это реквизит объекта обработки
ТЧИсточник = ЭтотОбъект.Товары;
// Получаем ссылку на табличную часть документа, которую будем заполнять
ТЧПриемник = Док.Товары;
Теперь, когда у нас есть ссылки на обе табличные части, мы можем пройтись по каждой строке исходной табличной части и добавить новую строку в табличную часть документа. Для этого используем цикл Для Каждого.
Для Каждого СтрокаИсточника Из ТЧИсточник Цикл
// Добавляем новую строку в табличную часть документа-приемника
НоваяСтрокаПриемника = ТЧПриемник.Добавить();
// Здесь будет код для заполнения реквизитов НоваяСтрокаПриемника
КонецЦикла;
Внутри этого цикла мы будем определять, как именно заполнить реквизиты НоваяСтрокаПриемника на основе данных из СтрокаИсточника. Рассмотрим основные методы заполнения.
Этот метод является самым простым и очевидным, если имена и типы реквизитов в исходной и целевой табличных частях совпадают. Мы просто присваиваем значение каждого реквизита из исходной строки в соответствующий реквизит новой строки.
Например, если в обеих табличных частях есть реквизит с именем Номенклатура, мы можем сделать так:
Для Каждого СтрокаИсточника Из ТЧИсточник Цикл
НоваяСтрокаПриемника = ТЧПриемник.Добавить();
НоваяСтрокаПриемника.Номенклатура = СтрокаИсточника.Номенклатура;
НоваяСтрокаПриемника.Количество = СтрокаИсточника.Количество;
НоваяСтрокаПриемника.Цена = СтрокаИсточника.Цена;
// ... и так далее для всех нужных реквизитов
КонецЦикла;
Этот подход хорошо работает, когда структуры табличных частей почти идентичны.
ЗаполнитьЗначенияСвойств()Функция ЗаполнитьЗначенияСвойств() – это мощный инструмент для копирования значений одноименных свойств из одного объекта в другой. Она значительно упрощает код, если требуется скопировать множество реквизитов, имена которых совпадают в источнике и приемнике.
Правильное применение ЗаполнитьЗначенияСвойств() для строк табличных частей выглядит следующим образом:
Для Каждого СтрокаИсточника Из ТЧИсточник Цикл
НоваяСтрокаПриемника = ТЧПриемник.Добавить();
// Копируем все одноименные свойства из СтрокаИсточника в НоваяСтрокаПриемника
ЗаполнитьЗначенияСвойств(НоваяСтрокаПриемника, СтрокаИсточника);
КонецЦикла;
Вы также можете указать, какие свойства нужно копировать, передав их список третьим параметром:
Для Каждого СтрокаИсточника Из ТЧИсточник Цикл
НоваяСтрокаПриемника = ТЧПриемник.Добавить();
// Копируем только свойства "Номенклатура", "Количество", "Цена"
ЗаполнитьЗначенияСвойств(НоваяСтрокаПриемника, СтрокаИсточника, "Номенклатура,Количество,Цена");
КонецЦикла;
Или, наоборот, исключить некоторые свойства:
Для Каждого СтрокаИсточника Из ТЧИсточник Цикл
НоваяСтрокаПриемника = ТЧПриемник.Добавить();
// Копируем все свойства, кроме "УникальныйИдентификатор"
ЗаполнитьЗначенияСвойств(НоваяСтрокаПриемника, СтрокаИсточника, , "УникальныйИдентификатор");
КонецЦикла;
Важный нюанс: В исходных сообщениях форума была попытка использовать ЗаполнитьЗначенияСвойств(ТЧПриемник,ТЧИсточник);. Это некорректно, поскольку функция предназначена для копирования свойств объектов, а не для копирования коллекций строк. Она не перебирает строки автоматически и не добавляет их в приемник. Поэтому всегда применяйте эту функцию к отдельным строкам: ЗаполнитьЗначенияСвойств(НоваяСтрокаПриемника, СтрокаИсточника);.
Часто возникает ситуация, когда в исходной табличной части обработки и в целевой табличной части документа реквизиты либо имеют разные имена, либо разные типы данных. Разберем эти случаи.
Если в табличной части обработки колонка называется, например, НаименованиеТовара, а в документе она должна быть Номенклатура, то прямое присвоение или ЗаполнитьЗначенияСвойств() не сработает автоматически. В этом случае мы должны явно указать соответствие при прямом присвоении:
Для Каждого СтрокаИсточника Из ТЧИсточник Цикл
НоваяСтрокаПриемника = ТЧПриемник.Добавить();
НоваяСтрокаПриемника.Номенклатура = СтрокаИсточника.НаименованиеТовара; // Явное сопоставление
// ...
КонецЦикла;
Одна из самых распространенных ситуаций – это когда в исходной табличной части обработки реквизит Номенклатура имеет тип Строка (например, пользователь вводил наименование товара текстом), а в документе этот реквизит должен быть ссылкой на элемент справочника Справочник.Номенклатура.
В этом случае простое присвоение НоваяСтрокаПриемника.Номенклатура = СтрокаИсточника.Номенклатура; вызовет ошибку типа, так как система ожидает ссылку, а получает строку.
Чтобы решить эту проблему, нам нужно найти соответствующий элемент справочника по его строковому наименованию. Для этого мы используем метод НайтиПоНаименованию() справочника:
Для Каждого СтрокаИсточника Из ТЧИсточник Цикл
НоваяСтрокаПриемника = ТЧПриемник.Добавить();
// Предполагаем, что СтрокаИсточника.Номенклатура имеет тип 'Строка'
// А НоваяСтрокаПриемника.Номенклатура должен быть 'СправочникСсылка.Номенклатура'
// Находим элемент справочника Номенклатура по его наименованию
НайденнаяНоменклатура = Справочники.Номенклатура.НайтиПоНаименованию(СтрокаИсточника.Номенклатура);
Если НайденнаяНоменклатура <> Неопределено Тогда
// Если элемент найден, присваиваем ссылку
НоваяСтрокаПриемника.Номенклатура = НайденнаяНоменклатура;
Иначе
// Обработка ситуации, когда номенклатура не найдена
// Например, можно вывести сообщение пользователю, пропустить строку
// или создать новый элемент справочника (это уже более сложная логика)
Сообщить("Номенклатура '" + СтрокаИсточника.Номенклатура + "' не найдена в справочнике!");
КонецЕсли;
// Заполняем остальные реквизиты, которые могут быть разных типов, но требуют прямого присвоения или преобразования
НоваяСтрокаПриемника.Количество = СтрокаИсточника.Количество;
// ...
КонецЦикла;
Важные замечания по НайтиПоНаименованию():
НайтиПоНаименованию() ищет точное совпадение наименования. Любые опечатки, лишние пробелы, разные регистры (если не использовать дополнительную обработку строки перед поиском) приведут к тому, что элемент не будет найден.НайтиПоНаименованию() может быть медленным. Для оптимизации можно предварительно загрузить часто используемые элементы справочника в таблицу значений или соответствие.НайтиПоНаименованию() вернет первый найденный. Это может привести к некорректному заполнению. В таких случаях требуется более сложная логика поиска (например, по наименованию и артикулу).Выгрузить() и Загрузить() для табличных частейДля случаев, когда структуры исходной и целевой табличных частей (или таблицы значений) почти полностью совпадают, самый производительный и элегантный способ – это использование методов Выгрузить() и Загрузить(). Эти методы позволяют преобразовать табличную часть в таблицу значений и обратно.
Этот подход особенно хорош для больших объемов данных.
// Выгружаем данные из табличной части обработки в ТаблицуЗначений
// Можно указать список колонок, если нужно выгрузить не все
// Например: ТЗИсточник = ТЧИсточник.Выгрузить(, "Номенклатура, Количество, Цена");
ТЗИсточник = ТЧИсточник.Выгрузить();
// Загружаем данные из ТаблицыЗначений напрямую в табличную часть документа
ТЧПриемник.Загрузить(ТЗИсточник);
Преимущества этого метода:
Ограничения:
Пример модификации ТаблицыЗначений перед загрузкой, если нужно поменять тип колонки:
// Выгружаем данные из табличной части обработки
ТЗИсточник = ЭтотОбъект.Товары.Выгрузить();
// Если Номенклатура в ТЗИсточник - строка, а в ТЧПриемник должна быть ссылка
// Добавляем новую колонку для ссылок
ТЗИсточник.Колонки.Добавить("НоменклатураСсылка", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
Для Каждого СтрокаТЗ Из ТЗИсточник Цикл
НайденнаяНоменклатура = Справочники.Номенклатура.НайтиПоНаименованию(СтрокаТЗ.Номенклатура);
Если НайденнаяНоменклатура <> Неопределено Тогда
СтрокаТЗ.НоменклатураСсылка = НайденнаяНоменклатура;
Иначе
Сообщить("Номенклатура '" + СтрокаТЗ.Номенклатура + "' не найдена!");
КонецЕсли;
КонецЦикла;
// Удаляем старую строковую колонку "Номенклатура"
ТЗИсточник.Колонки.Удалить("Номенклатура");
// Переименовываем новую колонку, чтобы она соответствовала имени в документе
ТЗИсточник.Колонки.НоменклатураСсылка.Имя = "Номенклатура";
// Загружаем модифицированную ТаблицуЗначений в табличную часть документа
Док.Товары.Загрузить(ТЗИсточник);
После того как табличная часть документа заполнена, мы можем открыть его форму для просмотра и возможной ручной корректировки пользователем. Также важно не забыть записать документ.
// Открываем форму созданного документа
Форма = Док.ПолучитьФорму("ФормаДокумента");
Форма.Открыть();
// Если не требуется взаимодействие с пользователем, можно сразу записать документ:
// Док.Записать(РежимЗаписиДокумента.Запись); // Просто запись
// Док.Записать(РежимЗаписиДокумента.Проведение); // Запись и проведение
Обратите внимание, что вызов Док.Записать() может быть сделан до открытия формы, если цель – просто программно создать и записать документ без показа пользователю.
ЭлементыФормы) – на клиенте. Если ваш код выполняется на клиенте, но обращается к данным объектов, убедитесь, что он вызывается из серверного контекста (например, через директиву &НаСервере).ПриИзменении для реквизита строки). Если логика конфигурации опирается на эти обработчики, их, возможно, придется вызывать явно после заполнения или учитывать, что часть логики может не выполниться.Мы подробно рассмотрели различные подходы к заполнению табличной части документа из табличной части обработки. Выбирайте наиболее подходящий метод в зависимости от сложности сопоставления данных, объемов информации и требований к производительности. Успехов в разработке на 1С!