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

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

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

Выясняем причину ошибки «Обращение к процедуре объекта как к функции»

Для начала проанализируем ситуацию, в которой оказывается разработчик. Ошибка чаще всего возникает в двух случаях. Первый — это попытка использовать метод Записать() в правой части оператора присваивания или в условии. Например, если написать Результат = Объект.Записать(), система выдаст ошибку, так как в 1С метод Записать() для справочников и документов является процедурой, а не функцией, и он не возвращает никакого значения.

Второй, более коварный случай связан с работой отладчика. Рассмотрим подробнее: если вы остановились на точке останова и в окне «Вычислить выражение» (Shift+F9) ввели Объект.Записать(), система выдаст ровно ту же ошибку. Отладчик ожидает, что выражение вернет результат, который можно отобразить в табло. Поскольку процедура ничего не возвращает, выполнение прерывается с технической ошибкой. Проанализируем ситуацию: если ваш код выполняется, данные записываются, но ошибка видна только в окне отладки — значит, с самим программным кодом всё в порядке, и вы просто пытаетесь «вычислить» невычисляемое.

Шаг 1. Получение выделенных строк на клиенте

Разберем процесс реализации задачи с самого начала. Прежде всего, нам нужно понять, какие именно строки пользователь выделил в табличной части документа. Для этого мы будем использовать свойство таблицы формы ВыделенныеСтроки. Важно помнить, что это коллекция идентификаторов, а не сами данные строк.

Рассмотрим пример кода на клиенте, который инициирует процесс:


&НаКлиенте
Процедура ИзменитьКачество(Команда)
    
    // Получаем массив идентификаторов выделенных строк
    ВыделенныеСтроки = Элементы.Товары.ВыделенныеСтроки;
    
    Если ВыделенныеСтроки.Количество() = 0 Тогда
        ПоказатьПредупреждение(, "Пожалуйста, выделите хотя бы одну строку в списке!");
        Возврат;
    КонецЕсли;
    
    // Запрашиваем у пользователя новое значение
    // Используем асинхронный ввод для обеспечения работы в веб-клиенте
    ОписаниеОповещения = Новый ОписаниеОповещения("ПослеВыбораЗначенияКачества", ЭтотОбъект, ВыделенныеСтроки);
    ПоказатьВводЗначения(ОписаниеОповещения, ПредопределенноеЗначение("Перечисление.Качество.Новый"), "Выберите новое качество");
    
КонецПроцедуры

Шаг 2. Обработка выбора значения и передача на сервер

После того как пользователь выбрал значение в диалоговом окне, управление передается в процедуру обратного вызова. Здесь мы проверим, было ли выбрано значение (пользователь мог нажать «Отмена»), и если всё в порядке — вызовем серверную процедуру для непосредственного изменения данных в базе.


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

Шаг 3. Реализация серверной логики изменения справочника

Теперь перейдем к самой ответственной части — работе с объектами базы данных. Нам необходимо итерировать массив переданных идентификаторов, находить соответствующие ссылки на характеристики и изменять их реквизиты. Посмотрим на пример реализации серверной функции:


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

Оптимизация: транзакции и режим обмена данными

Рассмотрим подробнее вопросы производительности и надежности. Если строк для изменения много (например, сотни или тысячи), выполнение записи каждого объекта по отдельности может занять значительное время и нагрузить систему. Проанализируем ситуацию: каждая запись Записать() инициирует отдельную транзакцию в СУБД, вызывает проверки в модуле объекта и обновляет индексы.

Чтобы ускорить процесс, воспользуемся следующими приемами:

  1. Использование явной транзакции: Обернув цикл в конструкцию НачатьТранзакцию() — ЗафиксироватьТранзакцию(), мы добьемся того, что база данных выполнит все изменения как одну операцию. Это значительно быстрее и гарантирует целостность: либо изменятся все характеристики, либо ни одна (в случае ошибки).
  2. Отключение лишних проверок: Если мы уверены в корректности данных, можно использовать свойство ОбменДанными.Загрузка = Истина. Это позволит миновать прикладные проверки в процедурах ПередЗаписью модуля справочника, что существенно ускорит запись.

Посмотрим на оптимизированный код:


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

Важное архитектурное замечание

Проанализируем последствия наших действий. Справочник ХарактеристикиНоменклатуры является общим для всей системы. Это означает, что если одна и та же характеристика (например, «Красный, размер 42») выбрана в десяти разных документах (поступлениях, реализациях, заказах), то изменение её реквизита «Качество» отразится во всех этих документах одновременно.

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

Резюме для закрепления материала

Подведем итоги нашего разбора:

Следуя этим правилам, вы сможете создавать надежные и быстрые инструменты для автоматизации работы пользователей в 1С.

← На главную