Как заполнить табличную часть документа на основе нескольких Заказов покупателя в 1С 8.3

Программист 1С v8.3 (Управляемые формы) 1С:Управление нашей фирмой Управленческий учет IT и автоматизация бизнеса
← На главную

В разработке на платформе 1С:Предприятие 8.3 часто возникает задача заполнения табличной части (ТЧ) одного документа данными из других документов-источников. Типичный пример — документ Сдельный наряд, в который необходимо перенести перечень номенклатуры (работ или услуг) из одного или сразу нескольких документов Заказ покупателя.

В этой статье мы подробно разберем, как спроектировать и реализовать такой механизм на управляемых формах. Мы рассмотрим три различных сценария решения этой задачи, начиная от интерактивного множественного выбора и заканчивая заполнением «на лету» прямо из строки таблицы, а также разберем лучшие практики оптимизации серверных вызовов.

Вариант 1. Множественный выбор заказов по кнопке (рекомендуемый способ)

Этот способ является наиболее удобным и эргономичным для пользователей. Мы добавим на командную панель нашей табличной части кнопку, которая будет открывать форму списка заказов с возможностью выделения нескольких документов. После подтверждения выбора данные из всех выделенных заказов автоматически перенесутся в нашу табличную часть.

Для реализации этого механизма нам потребуется связать клиентскую часть формы (интерактивный выбор документов пользователем) и серверную часть (оптимальное чтение данных из базы данных).

Шаг 1. Клиентская часть (&НаКлиенте)

В современном интерфейсе 1С («Такси») категорически не рекомендуется использовать устаревшие модальные окна (такие как ОткрытьФормуМодально), так как они не поддерживаются в веб-клиенте и мобильной платформе. Вместо этого мы применим асинхронный подход с использованием объекта ОписаниеОповещения.

Создадим команду формы ЗаполнитьПоЗаказам, вынесем ее на командную панель табличной части и пропишем следующий код:


&НаКлиенте
Процедура КомандаЗаполнитьПоЗаказам(Команда)
	
	ПараметрыВыбора = Новый Структура;
	// Активируем режим множественного выбора в открываемой форме
	ПараметрыВыбора.Вставить("МножественныйВыбор", Истина);
	
	// Указываем процедуру, которая будет вызвана после закрытия формы выбора
	Оповещение = Новый ОписаниеОповещения("ОбработатьВыборЗаказов", ЭтотОбъект);
	
	// Открываем форму выбора заказов покупателей асинхронно
	ОткрытьФорму(
		"Документ.ЗаказПокупателя.ФормаВыбора", 
		ПараметрыВыбора, 
		ЭтотОбъект, 
		СовместимостьКлиентаИСервера.УникальныйИдентификатор, 
		, 
		, 
		Оповещение
	);
	
КонецПроцедуры

&НаКлиенте
Процедура ОбработатьВыборЗаказов(РезультатВыбора, ДополнительныеПараметры) Экспорт
	
	// Если пользователь передумал и закрыл форму выбора, ничего не делаем
	Если РезультатВыбора = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	// Формируем массив выбранных ссылок документов
	МассивЗаказов = Новый Массив;
	Если ТипЗнч(РезультатВыбора) = Тип("Массив") Тогда
		МассивЗаказов = РезультатВыбора;
	Иначе
		// Если выбран всего один документ, платформа может вернуть его ссылкой, а не массивом
		МассивЗаказов.Добавить(РезультатВыбора);
	КонецЕсли;
	
	// Вызываем серверную процедуру для быстрого переноса строк
	ЗаполнитьТабличнуюЧастьНаСервере(МассивЗаказов);
	
КонецПроцедуры

Шаг 2. Оптимальная серверная часть (&НаСервере)

Проанализируем ключевую ошибку начинающих разработчиков: обход выбранных документов в цикле на сервере и обращение к их табличным частям через точку (например, Заказ.Товары). Такой подход порождает так называемую проблему "N+1 запросов", перегружает сетевой трафик между сервером приложений и СУБД и драматически замедляет работу системы.

Правильный и профессиональный подход — прочитать все данные табличных частей выбранных документов одним запросом. Посмотрим на пример реализации:


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

---

Вариант 2. Выбор заказа прямо внутри строки табличной части

Иногда бизнес-логика требует, чтобы пользователь добавлял заказы построчно непосредственно в самой табличной части. При выборе документа в колонке Заказ покупателя система должна автоматически «развернуть» этот заказ, подставив его номенклатуру в эту же строку, а при наличии нескольких позиций в заказе — автоматически сгенерировать новые строки ниже.

Рассмотрим, как это реализовать с помощью события ПриИзменении колонки табличной части.

Код обработчика на клиенте и сервере:


&НаКлиенте
Процедура РаботыЗаказПокупателяПриИзменении(Элемент)
	
	ТекущаяСтрока = Элементы.Работы.ТекущиеДанные;
	Если ТекущаяСтрока = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	// Проверяем, заполнил ли пользователь поле заказа
	Если ЗначениеЗаполнено(ТекущаяСтрока.ЗаказПокупателя) Тогда
		// Передаем ссылку на выбранный заказ и уникальный идентификатор строки на сервер
		ЗаполнитьСтрокиИзЗаказаНаСервере(ТекущаяСтрока.ЗаказПокупателя, ТекущаяСтрока.ПолучитьИдентификатор());
	КонецЕсли;
	
КонецПроцедуры

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

---

Вариант 3. Использование БСП (Библиотека стандартных подсистем)

Если вы работаете в типовой или кастомизированной конфигурации, созданной на базе современных решений 1С (УНФ, УТ, КА, ERP), то наиболее правильным архитектурным решением будет создание Внешней обработки заполнения табличных частей. Этот подход реализуется через подсистему «Дополнительные отчеты и обработки» БСП.

Выделим ключевые преимущества использования БСП:

  1. Чистота конфигурации. Вам не требуется снимать документ или его форму с поддержки и вносить изменения в типовой код. Это значительно облегчает последующие обновления программы.
  2. Гибкость настройки. Вы можете в любой момент обновить алгоритм заполнения через режим предприятия, просто обновив файл внешней обработки, без монопольного доступа к базе данных.
  3. Интерфейс «из коробки». БСП автоматически добавит кнопку заполнения в стандартное меню «Заполнить» над табличной частью или формой документа.

Для создания такой обработки достаточно настроить функцию СведенияОВнешнейОбработке() в модуле объекта внешней обработки, указав вид ДополнительнаяОбработкаЗаполнения, и прописать алгоритм загрузки данных в экспортной процедуре ВыполнитьКоманду.

---

Важные правила оптимизации при работе с табличными частями

Выясним основные нюансы производительности, которые уберегут вашу базу данных от зависаний при работе под нагрузкой:

Применяя описанные методики, вы сможете создавать отзывчивые, быстрые и удобные интерфейсы для работы ваших пользователей с документами в системе 1С:Предприятие 8.3.

← На главную