Как программно получить значения новых цен из динамической таблицы в документе «Установка цен номенклатуры»

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

В процессе разработки расширений для типовых конфигураций (таких как 1С:Розница, 1С:Управление торговлей 11 или 1С:ERP) программисты часто сталкиваются с необходимостью кастомизации документа УстановкаЦенНоменклатуры. Одной из самых популярных задач является расчет средневзвешенной цены: когда новая цена должна рассчитываться исходя из остатков на складе и цен в документе поступления. Однако при попытке программно прочитать «Новую цену» прямо из формы документа возникает сложность: колонки с ценами в ДеревоЦен создаются динамически, и к ним невозможно обратиться по статичному имени.

В этой статье мы подробно разберем, как устроено ДеревоЦен, почему обычные методы обращения к реквизитам не работают и как правильно «вытащить» данные из динамических колонок для выполнения расчетов.

Анализ проблемы: почему не работает прямое обращение

Рассмотрим ситуацию: нам необходимо реализовать расчет по формуле: (СтараяЦена * Остаток + НоваяЦена * Количество) / ОбщееКоличество. Проблема заключается в том, что в конфигураторе у объекта ДеревоЦен (тип ДанныеФормыДерево) есть только стандартные реквизиты: Номенклатура, Характеристика и т.д. Колонки для конкретных видов цен (например, «Розничная», «Оптовая») генерируются платформой программно в момент открытия формы или выбора видов цен пользователем.

Проанализируем структуру имен этих колонок. Если мы попробуем посмотреть их через отладчик, то увидим идентификаторы вида Цена_37f5b1... или Розничная1111. Эти имена не являются константами и могут меняться. Следовательно, код типа пСтрДеревоЦен.Цена вызовет ошибку «Поле объекта не обнаружено».

Шаг 1: Изучение механизма динамических колонок

Для того чтобы получить доступ к данным, нам нужно понять, где система хранит карту соответствия между объектом ВидЦены и физическим именем колонки на форме. В типовых решениях 1С для этого используется вспомогательная коллекция ВыбранныеЦены. Это таблица на форме, которая содержит информацию о том, какие виды цен сейчас отображаются в дереве.

Обычно эта коллекция имеет следующие поля:

  1. Ссылка — ссылка на справочник ВидыЦен.
  2. ИмяКолонки — то самое строковое имя реквизита, которое нам нужно для обращения к дереву.
  3. Валюта — валюта данного вида цены.

Шаг 2: Реализация функции получения имени колонки

Рассмотрим метод, который позволит нам по конкретному виду цены узнать программное имя его колонки. Это критически важный шаг, без которого мы не сможем обратиться к данным в цикле.


Функция ПолучитьИмяКолонкиПоВидуЦен(ВидЦены)
    // Ищем в коллекции ВыбранныеЦены строку с нужным видом цены
    ПараметрыОтбора = Новый Структура("Ссылка", ВидЦены);
    НайденныеСтроки = ЭтаФорма.ВыбранныеЦены.НайтиСтроки(ПараметрыОтбора);
    
    Если НайденныеСтроки.Количество() > 0 Тогда
        Возврат НайденныеСтроки[0].ИмяКолонки;
    Иначе
        Возврат "";
    КонецЕсли;
КонецФункции

В данном примере мы используем метод НайтиСтроки для коллекции ВыбранныеЦены. Если вид цены найден, функция возвращает имя колонки, которое мы в дальнейшем используем как ключ доступа.

Шаг 3: Программный обход ДереваЦен и расчет значений

Теперь, когда у нас есть механизм определения имен колонок, разберем, как правильно организовать цикл по строкам ДеревоЦен и выполнить расчет. Важно помнить, что ДеревоЦен — это иерархическая структура, и нам нужно обрабатывать строки, содержащие номенклатуру.

Проанализируем следующий алгоритм:


Процедура РассчитатьСредневзвешенныеЦены()
    
    // 1. Получаем нужный вид цены (например, Розничная)
    ВидЦеныСсылка = Справочники.ВидыЦен.НайтиПоНаименованию("Розничная");
    ИмяКолонкиНовойЦены = ПолучитьИмяКолонкиПоВидуЦен(ВидЦеныСсылка);
    
    Если ПустаяСтрока(ИмяКолонкиНовойЦены) Тогда
        Сообщить("Вид цены 'Розничная' не выбран в документе!");
        Возврат;
    КонецЕсли;

    // 2. Получаем элементы дерева
    ВсеСтрокиДерева = Объект.ДеревоЦен.ПолучитьЭлементы();

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

            Если (Остаток + КоличествоПоступление) > 0 Тогда
                СредняяЦена = (СтараяЦена * Остаток + НоваяЦена * КоличествоПоступление) / (Остаток + КоличествоПоступление);
                
                // Записываем результат в нужную колонку или вспомогательный реквизит
                ТекущаяСтрока.РеквизитКорректор = СредняяЦена;
            КонецЕсли;
            
        КонецЕсли;
        
        // Если есть подчиненные строки (характеристики), обходим их
        ОбходПодчиненныхСтрок(ТекущаяСтрока, ИмяКолонкиНовойЦены);
        
    КонецЦикла;

КонецПроцедуры

Важные нюансы при работе с данными формы

Рассмотрим несколько важных моментов, которые помогут избежать ошибок при реализации:

  1. Типы данных: Объект ТекущаяСтрока имеет тип ДанныеФормыЭлементДерева. Он ведет себя подобно структуре, что позволяет использовать синтаксис Строка["ИмяПоля"].
  2. Оптимизация запросов: Никогда не выполняйте запрос внутри цикла Для Каждого. Если вам нужны данные об остатках или старых ценах из регистра, сначала выгрузите всё дерево в таблицу значений, соберите список всей номенклатуры, выполните ОДИН запрос к базе данных, а затем соедините результаты в памяти.
  3. Характеристики номенклатуры: В типовых конфигурациях цены часто устанавливаются не на номенклатуру, а на связку Номенклатура + Характеристика. При поиске остатков и старых цен обязательно учитывайте характеристику, иначе расчет будет некорректным.
  4. Упаковки: Проверьте, в каких единицах измерения указана цена в дереве. Если используется упаковка с коэффициентом, отличным от единицы, данные необходимо привести к базовой единице хранения.

Использование типовых процедур общего назначения

Выясним, есть ли возможность упростить код. В модуле формы документа УстановкаЦенНоменклатуры часто присутствуют экспортные процедуры, такие как УстановкаЦенСервер.ПостроитьДеревоЦен. Если вы создаете расширение, вы можете «подсмотреть» в этих процедурах, как именно заполняется структура ПараметрыДанныхЦен. Иногда разработчики 1С уже хранят подготовленные соответствия видов цен и колонок в дополнительных свойствах объекта или в параметрах формы, что избавляет от необходимости ручного поиска в ВыбранныеЦены.

Подведем итог: Для доступа к динамическим ценам в 1С:Розница необходимо использовать коллекцию ЭтаФорма.ВыбранныеЦены для получения технического имени колонки, а затем обращаться к данным строки дерева через оператор []. Такой подход гарантирует стабильную работу кода даже при изменении состава отображаемых цен в документе.

← На главную