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