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

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

При разработке внешних обработок заполнения табличных частей для обычных форм в конфигурациях типа Управление торговлей 10.3, Управление производственным предприятием 1.3 или Комплексная автоматизация 1.1 программисты часто сталкиваются с нестандартным поведением документа Установка цен номенклатуры. Физически данные документа хранятся в табличной части Товары, однако на форме пользователю отображается совсем другая таблица — реквизит формы ТаблицаЦен. Это сделано для того, чтобы транспонировать строки в удобную сетку, где типы цен отображаются в виде колонок.

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

В чем сложность архитектуры документа Установка цен номенклатуры

Давайте проанализируем внутреннее устройство этого документа. В базе данных табличная часть Товары является «плоской». Если для одного товара мы устанавливаем три разных типа цен (например, Оптовая, Розничная и Дистрибьюторская), то в базе данных будут созданы три отдельные строки. В каждой из них будет указан свой тип цен и своя цена.

Чтобы пользователь не сошел с ума от ввода данных в плоскую таблицу, разработчики фирмы 1С создали на форме динамическую таблицу значений ТаблицаЦен. Она объединяет строки по номенклатуре и характеристикам, а типы цен выводит в динамические колонки. Связующим звеном между плоской таблицей базы данных и экранной формой служит служебный реквизит ИндексСтрокиТаблицыЦен.

Если мы просто заполним табличную часть Товары нашего объекта во внешней обработке, экранная форма документа ничего об этом не узнает. Данные появятся на экране только после физического закрытия и повторного открытия формы. Попытка автоматизировать это через программное закрытие и открытие формы приводит к ошибке общего модуля УниверсальныеМеханизмы:

Ошибка при вызове метода контекста (Добавить): Не уникальное имя!

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

---

Способ 1. Синхронизация экранной таблицы цен вручную

Если нам необходимо обновить интерфейс «на лету» без деструктивного переоткрытия формы, мы можем вручную пересчитать и заполнить реквизит формы ТаблицаЦен, а затем заставить платформу перерисовать колонки.

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


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

Для корректного отображения колонок "Код" и "Артикул", которые могут сбиваться при вызове метода СоздатьКолонки(), добавим вспомогательную процедуру управления их видимостью:


Процедура УстановитьВидимостьКолонокКодаАртикула(ФормаДокумента)
    
    ПоказыватьКод = ВосстановитьЗначение("УстановкаЦенНоменклатуры_ПоказыватьКод");
    ПоказыватьАртикул = ВосстановитьЗначение("УстановкаЦенНоменклатуры_ПоказыватьАртикул");
    
    ПоказыватьКод = ?(ПоказыватьКод = Неопределено, Ложь, ПоказыватьКод);
    ПоказыватьАртикул = ?(ПоказыватьАртикул = Неопределено, Ложь, ПоказыватьАртикул);
    
    КолонкиТаблицы = ФормаДокумента.ЭлементыФормы.ТаблицаЦен.Колонки;
    
    Если КолонкиТаблицы.Найти("Код") <> Неопределено Тогда
        КолонкиТаблицы.Код.Видимость = ПоказыватьКод;
    КонецЕсли;
    
    Если КолонкиТаблицы.Найти("Артикул") <> Неопределено Тогда
        КолонкиТаблицы.Артикул.Видимость = ПоказыватьАртикул;
    КонецЕсли;
    
КонецПроцедуры

---

Способ 2. Использование штатного метода обновления формы (Элегантный путь)

Давайте выясним, можно ли сделать это проще и изящнее, не дублируя алгоритмы построения колонок вручную. В типовых конфигурациях 1С разработчики уже заложили механизмы для синхронизации экранной таблицы с физической табличной частью. На форме документа Установка цен номенклатуры присутствует экспортный метод ЗаполнитьТаблицуЦенФормы().

Использование этого метода существенно упрощает нам задачу: нам больше не требуется вручную перебирать соответствия колонок, очищать ТаблицаЦен и управлять видимостью артикулов. Система сделает это сама в полном соответствии со своими внутренними алгоритмами.

Посмотрим на пример реализации обработки с использованием штатного метода формы:


Процедура Инициализировать(ДокументОбъект, ИмяТабличнойЧасти, ТабличноеПолеОбъекта = Неопределено) Экспорт
    
    ТекФорма = ДокументОбъект.ПолучитьФорму("ФормаДокумента");
    ТЧТовары = ДокументОбъект.Товары;
    
    // Очищаем табличную часть документа
    Если ТЧТовары.Количество() > 0 Тогда
        ТекстВопроса = "Перед заполнением табличная часть будет очищена. Заполнить?";
        Ответ = Вопрос(ТекстВопроса, РежимДиалогаВопрос.ДаНет, , , "Внимание");
        Если Ответ = КодВозвратаДиалога.Нет Тогда 
            Возврат; 
        КонецЕсли;
        ТЧТовары.Очистить();
    КонецЕсли;
    
    ФормаВыбора = ПолучитьФорму("Форма");
    Если ФормаВыбора.ОткрытьМодально() = "ОК" Тогда
        
        ТекущийИндексСтроки = 0; 
        
        Для Каждого СтрокаИзExcel Из ТаблицаДанныхИзExcel Цикл
            
            НоменклатураСсылка = Справочники.Номенклатура.НайтиПоНаименованию(СтрокаИзExcel.Наименование, Истина);
            
            Если ЗначениеЗаполнено(НоменклатураСсылка) Тогда
                
                // Для каждой позиции номенклатуры создаем строки под все типы цен
                Для Каждого СтрокаТипаЦен Из ДокументОбъект.ТипыЦен Цикл
                    
                    НоваяСтрока = ТЧТовары.Добавить();
                    НоваяСтрока.Номенклатура = НоменклатураСсылка;
                    НоваяСтрока.ХарактеристикаНоменклатуры = Справочники.ХарактеристикиНоменклатуры.ПустаяСсылка();
                    
                    НоваяСтрока.ТипЦен = СтрокаТипаЦен.ТипЦен;
                    НоваяСтрока.Валюта = СтрокаТипаЦен.ТипЦен.ВалютаЦены;
                    НоваяСтрока.Цена = СтрокаИзExcel.Цена; 
                    
                    НоваяСтрока.ЕдиницаИзмерения = НоменклатураСсылка.ЕдиницаХраненияОстатков;
                    НоваяСтрока.Коэффициент = НоваяСтрока.ЕдиницаИзмерения.Коэффициент;
                    
                    // Заполняем ключевое связующее поле индекса
                    НоваяСтрока.ИндексСтрокиТаблицыЦен = ТекущийИндексСтроки;
                    
                КонецЦикла;
                
                ТекущийИндексСтроки = ТекущийИндексСтроки + 1;
                
            Иначе
                Сообщить("В базе данных отсутствует номенклатура: " + СтрокаИзExcel.Наименование);
            КонецЕсли;
        КонецЦикла;
        
        // Вызываем штатный метод формы для мгновенного обновления интерфейса!
        ТекФорма.ЗаполнитьТаблицуЦенФормы();
        
    КонецЕсли;
    
КонецПроцедуры

Преимущества второго способа

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

← На главную