Как программно заполнить субконто в табличной части 1С и избежать надписи "не требуется"?

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

При автоматизации работы в среде «1С:Предприятие 8» разработчики часто сталкиваются с ситуацией, когда после программного заполнения табличной части документа (например, «Отчет производства за смену», «Поступление товаров и услуг» или «Реализация товаров и услуг») визуальные поля для ввода субконто отображаются некорректно. Вместо привычных полей выбора справочников (контрагенты, номенклатурные группы, статьи затрат) пользователь видит серую надпись <не требуется>. При этом сами значения субконто могут быть физически записаны в базу данных, но на форме они не отображаются до тех пор, пока пользователь вручную не перевыберет счет затрат.

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

Выясняем причину: как устроена связь по типу в 1С

Для того чтобы понять природу проблемы, проанализируем, как платформа «1С:Предприятие» отображает аналитику на форме. Поля субконто (например, Субконто1, Субконто2, Субконто3) по умолчанию имеют составной тип данных, включающий десятки различных справочников и документов. Если бы платформа выводила их «как есть», пользователю при нажатии на кнопку выбора пришлось бы каждый раз вручную выбирать нужный тип данных из огромного списка. Чтобы этого избежать, разработчики платформы используют механизм Связи по типу.

В свойствах элемента формы (поля ввода субконто) настраивается связь по типу с другим реквизитом строки — видом субконто. В типовых конфигурациях 1С (таких как ERP, Комплексная автоматизация, Бухгалтерия предприятия) в табличных частях документов присутствуют скрытые служебные реквизиты:

  1. ВидСубконто1
  2. ВидСубконто2
  3. ВидСубконто3

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

Если же мы заполняем табличную часть программно на сервере, мы часто присваиваем значения только реквизитам СчетЗатрат и Субконто1, забывая заполнить служебные реквизиты ВидСубконто1. Так как виды субконто в строке остаются пустыми (Неопределено), платформа считает, что для данной строки аналитика не настроена, и выводит надпись <не требуется>.

Почему идея «вызвать ПриИзменении в цикле» ошибочна?

Многие начинающие разработчики пытаются решить проблему «в лоб»: обойти строки таблицы формы на клиенте и программно вызвать процедуру-обработчик СчетЗатратПриИзменении. Давайте разберем, почему этот путь ведет к архитектурному тупику:

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

Способ 1. Программное заполнение видов субконто на сервере

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

Посмотрим на пример кода реализации данного алгоритма на сервере:


&НаСервере
Процедура ЗаполнитьТабличнуюЧастьНаСервере()
    
    // Получаем коллекцию строк нашей таблицы на форме
    ТаблицаУслуг = Объект.Услуги;
    
    // Предположим, у нас есть подготовленная таблица значений с данными для заполнения
    ТаблицаДанныхЗаполнения = ПолучитьТаблицуДляЗаполнения();
    
    Для Каждого СтрокаДанных Из ТаблицаДанныхЗаполнения Цикл
        
        НоваяСтрока = ТаблицаУслуг.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаДанных);
        
        // Обязательно проверяем, заполнен ли счет затрат
        Если ЗначениеЗаполнено(НоваяСтрока.СчетЗатрат) Тогда
            
            // Получаем виды субконто выбранного счета из базы данных
            ВидыСубконтоСчета = НоваяСтрока.SчетЗатрат.ВидыСубконто;
            
            // Пробегаем по доступным видам субконто счета (их может быть до 3-х штук)
            Для Индекс = 0 По ВидыСубконтоСчета.Количество() - 1 Цикл
                
                НомерСубконто = Индекс + 1;
                ТекущийВидСубконто = ВидыСубконтоСчета[Индекс].ВидСубконто;
                
                // Заполняем скрытый реквизит вида субконто, чтобы сработала связь по типу
                НоваяСтрока["ВидСубконто" + НомерСубконто] = ТекущийВидСубконто;
                
                // Проверяем, подходит ли переданное значение субконто под тип этого вида субконто
                ИмяКолонкиЗначения = "Субконто" + НомерСубконто;
                Если СтрокаДанных.Свойство(ИмяКолонкиЗначения) Тогда
                    ЗначениеСубконто = СтрокаДанных[ИмяКолонкиЗначения];
                    
                    Если ЗначениеЗаполнено(ЗначениеСубконто) 
                        И ТекущийВидСубконто.ТипЗначения.СодержитТип(ТипЗнч(ЗначениеСубконто)) Тогда
                        
                        НоваяСтрока[ИмяКолонкиЗначения] = ЗначениеСубконто;
                    КонецЕсли;
                КонецЕсли;
                
            КонецЦикла;
            
        КонецЕсли;
        
    КонецЦикла;

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

При таком подходе, как только управление вернется на клиентскую сторону, платформа автоматически сопоставит типы благодаря заполненным полям ВидСубконто1, ВидСубконто2 и т.д. Поля отобразятся корректно, и вместо надписи <не требуется> пользователь увидит заполненные значения аналитики.

Способ 2. Рефакторинг и вынесение бизнес-логики

Что делать, если в обработчике события СчетЗатратПриИзменении на форме написана сложная логика (например, автоматическое определение подразделений, заполнение статей по умолчанию, расчет налоговых ставок или сложные проверки)? Дублировать этот код в процедуру программного заполнения крайне нежелательно — это нарушает принцип разработки DRY (Don't Repeat Yourself).

В этом случае проанализируем архитектуру модуля формы и выполним рефакторинг:

  1. Выделим всю бизнес-логику по заполнению строки при изменении счета из клиентского обработчика события в отдельную экспортную процедуру на сервере без использования элементов управления интерфейсом.
  2. Передадим в эту процедуру строку табличной части как объект данных.

Рассмотрим структуру кода после правильного рефакторинга:


// 1. Наш интерактивный обработчик на клиенте теперь просто вызывает серверный метод
&НаКлиенте
Процедура ТаблицаУслугСчетЗатратПриИзменении(Элемент)
    
    СтрокаТаблицы = Элементы.ТаблицаУслуг.ТекущиеДанные;
    Если СтрокаТаблицы <> Неопределено Тогда
        ОбработатьИзменениеСчетаНаСервере(СтрокаТаблицы);
    КонецЕсли;
    
КонецПроцедуры

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

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

Теперь эту же процедуру ОбработатьИзменениеСчетаНаСервере мы можем безопасно и эффективно вызывать в цикле программного заполнения ТЧ на сервере:


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

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

← На главную