При работе в конфигурации «1С:Розница 2.3» специалисты часто сталкиваются с необходимостью видеть актуальные остатки товаров непосредственно в документе УстановкаЦенНоменклатуры. Это позволяет товароведам принимать более взвешенные решения о наценках, опираясь на реальное количество товара на складах. Однако стандартная форма этого документа построена не на обычной табличной части, а на динамически формируемом Дереве Значений, что делает простую визуальную настройку невозможной. В этой статье мы подробно разберем, как решить эту задачу с помощью расширения, используя программное создание реквизитов и элементов формы.
Для начала проанализируем ситуацию. В документе УстановкаЦенНоменклатуры табличная часть Товары служит лишь источником данных, в то время как пользователь работает с объектом ДеревоЦен (реквизит формы). Это дерево перестраивается программно каждый раз, когда меняются виды цен или состав товаров. Основная сложность заключается в том, что при вызове типовой процедуры ПереформироватьТаблицуЦен система жестко завязывается на предопределенную структуру колонок. Если мы просто добавим колонку «в лоб», типовой код может вызвать исключение из-за несоответствия структур данных.
Рассмотрим пошагово, как внедрить наш функционал, не нарушая работу типовых алгоритмов.
Чтобы наша колонка появилась в интерфейсе, нам необходимо добавить реквизит в структуру дерева и создать соответствующий элемент формы (поле). Это лучше всего делать в событии ПриСозданииНаСервере формы документа через расширение.
Разберем пример кода, который создает колонку «Остаток»:
&НаСервере
Процедура Расш1_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
// Описываем новый реквизит для дерева цен
НовыеРеквизиты = Новый Массив;
ТипЧисло = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3));
НовыйРеквизит = Новый РеквизитФормы("Расш1_Остаток", ТипЧисло, "ДеревоЦен", "Остаток");
НовыеРеквизиты.Добавить(НовыйРеквизит);
// Добавляем реквизит в структуру формы
ЭтаФорма.ИзменитьРеквизиты(НовыеРеквизиты);
// Создаем визуальный элемент на форме в группе ДеревоЦен
НовыйЭлемент = Элементы.Добавить("Расш1_Остаток", Тип("ПолеФормы"), Элементы.ДеревоЦен);
НовыйЭлемент.Вид = ВидПоляФормы.ПолеНадписи; // Используем поле надписи, если редактирование не требуется
НовыйЭлемент.ПутьКДанным = "ДеревоЦен.Расш1_Остаток";
НовыйЭлемент.Заголовок = "Остаток";
КонецПроцедуры
Обратите внимание, что мы используем префикс Расш1_. Это критически важно для предотвращения конфликтов имен при обновлениях основной конфигурации.
Проанализируем процедуру ПереформироватьТаблицуЦен. Она полностью очищает и заново строит ДеревоЦен. Чтобы наш добавленный реквизит не вызывал ошибок при работе типового метода ЗначениеВРеквизитФормы, нам придется использовать перехват события. Наиболее надежный способ — временно удалять реквизит перед выполнением типового кода и восстанавливать его после.
Рассмотрим алгоритм в процедуре-перехватчике &Вместо:
&НаСервере
&Вместо("ПереформироватьТаблицуЦен")
Процедура Расш1_ПереформироватьТаблицуЦен(ПересчитатьЦены)
// 1. Сохраняем текущие значения остатков во временную таблицу,
// чтобы не потерять их при перестроении дерева
ТЗ_Данные = Новый ТаблицаЗначений;
ТЗ_Данные.Колонки.Добавить("Номенклатура");
ТЗ_Данные.Колонки.Добавить("Характеристика");
ТЗ_Данные.Колонки.Добавить("Остаток");
СохранитьЗначенияРеквизитаРекурсивно(ТЗ_Данные, ДеревоЦен.ПолучитьЭлементы());
// 2. Удаляем программный реквизит, чтобы типовой код его не "видел"
МассивУдаления = Новый Массив;
МассивУдаления.Добавить("ДеревоЦен.Расш1_Остаток");
ИзменитьРеквизиты(, МассивУдаления);
// 3. Вызываем стандартный механизм формирования цен
ПродолжитьВызов(ПересчитатьЦены);
// 4. Заново добавляем наш реквизит в структуру дерева
НовыеРеквизиты = Новый Массив;
НовыйРеквизит = Новый РеквизитФормы("Расш1_Остаток", Новый ОписаниеТипов("Число"), "ДеревоЦен", "Остаток");
НовыеРеквизиты.Добавить(НовыйРеквизит);
ИзменитьРеквизиты(НовыеРеквизиты);
// 5. Заполняем колонку данными (либо из сохраненной ТЗ, либо свежим запросом к базе)
ЗаполнитьОстаткиВДереве();
КонецПроцедуры
Просто добавить колонку недостаточно — ее нужно наполнить данными. Выясним причину, по которой не стоит делать запрос к регистру остатков в цикле по строкам дерева. Если в документе 500 позиций, 500 запросов существенно замедлят работу интерфейса. Правильный подход — получение остатков одним запросом по всему списку номенклатуры документа.
Рекомендуется создать вспомогательную процедуру ЗаполнитьОстаткиВДереве, которая выполнит следующие действия:
Номенклатура и Характеристика из ДеревоЦен в массив.ТоварыНаСкладах.Остатки с фильтром по этому массиву.Посмотрим на пример функции для обхода дерева:
&НаСервере
Процедура ЗаполнитьОстаткиРекурсивно(СтрокиДерева, СоответствиеОстатков)
Для Каждого Стр Из СтрокиДерева Цикл
Ключ = Строка(Стр.Номенклатура.УникальныйИдентификатор()) + "_" + Строка(Стр.Характеристика.УникальныйИдентификатор());
Если СоответствиеОстатков.Свойство(Ключ) Тогда
Стр.Расш1_Остаток = СоответствиеОстатков[Ключ];
КонецЕсли;
// Рекурсивный вызов для подчиненных узлов (характеристик или упаковок)
ЗаполнитьОстаткиРекурсивно(Стр.ПолучитьЭлементы(), СоответствиеОстатков);
КонецЦикла;
КонецПроцедуры
Проанализируем несколько технических нюансов, которые помогут сделать решение профессиональным:
НовыйЭлемент.ТолькоПросмотр = Истина. Это защитит данные от случайного изменения пользователем.Таким образом, мы рассмотрели полный цикл добавления информационной колонки в сложный динамический объект формы. Используя механизм ИзменитьРеквизиты и грамотное управление жизненным циклом формы в расширении, мы добились расширения функционала без снятия конфигурации с поддержки.