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