В процессе разработки в системе 1С:Предприятие часто возникает задача предоставить пользователю возможность изменять определенные параметры документов прямо из формы списка, не открывая каждый документ по отдельности. Это значительно ускоряет работу, например, при изменении статусов, приоритетов или ответственных лиц. В данной статье мы подробно разберем, как реализовать этот функционал, какие ошибки часто совершают начинающие разработчики и как правильно обрабатывать как одну строку, так и группу выделенных документов.
Прежде всего, проанализируем ситуацию, с которой столкнулся автор темы. Основная ошибка заключалась в попытке использовать директивы компиляции, такие как &НаКлиенте и &НаСервере, в обычной форме. Важно помнить, что обычные формы (характерные для платформы 8.2 и старых конфигураций на 8.3) работают в едином контексте, и использование этих директив там недопустимо. Если вы видите ошибку "Процедура или функция не определена" при явном наличии кода, первым делом проверьте тип формы, с которой вы работаете.
Рассмотрим ситуацию: у нас есть форма списка документов, и мы хотим по нажатию кнопки на форме изменить реквизит ПриоритетПлатежа у текущего выбранного документа.
Разберем алгоритм действий. Для того чтобы изменить данные в базе, нам недостаточно просто поменять значение в строке списка. Список — это лишь визуальное отображение данных (проекция). Нам необходимо получить ссылку на реальный документ, создать на её основе объект, внести изменения и записать его обратно в базу данных.
Посмотрим на корректный пример кода для обычной формы:
Процедура КомандаИзменитьПриоритетНажатие(Элемент)
// 1. Получаем текущие данные из табличного поля
ТекДанные = ЭлементыФормы.ДокументСписок.ТекущиеДанные;
// 2. Проверяем, что строка выбрана и она не пустая
Если ТекДанные <> Неопределено Тогда
// 3. Получаем объект документа по ссылке
Попытка
ДокОбъект = ТекДанные.Ссылка.ПолучитьОбъект();
// 4. Меняем значение нужного реквизита
// В данном случае используем перечисление
ДокОбъект.ПриоритетПлатежа = Перечисления.Приоритеты.ОченьВажный;
// 5. Записываем объект в базу данных
ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
// Сообщаем пользователю об успехе
Сообщить("Приоритет успешно изменен для документа: " + ДокОбъект);
Исключение
// Обработка блокировок или ошибок доступа
Сообщить("Не удалось изменить документ. Возможно, он заблокирован другим пользователем.");
КонецПопытки;
КонецЕсли;
КонецПроцедуры
Выясним причину, по которой метод ПолучитьОбъект() иногда вызывает ошибки. Часто разработчики пытаются вызвать его напрямую у переменной ТекДанные. Но ТекущиеДанные — это структура (строка списка), и метода получения объекта у нее нет. Он есть только у ссылки, поэтому обращение ТекДанные.Ссылка.ПолучитьОбъект() является единственно верным.
Часто пользователю требуется изменить реквизит не у одного документа, а сразу у нескольких, выделенных в списке (через клавиши Shift или Ctrl). Рассмотрим, как реализовать обход коллекции выделенных строк.
В обычных формах у табличного поля есть свойство ВыделенныеСтроки. Мы можем проитерировать этот список и выполнить запись для каждого документа. Проанализируем пример кода:
Процедура КнопкаИзменитьВыделенныеНажатие(Элемент)
// Получаем коллекцию выделенных ссылок
СписокВыделенных = ЭлементыФормы.ДокументСписок.ВыделенныеСтроки;
Если СписокВыделенных.Количество() = 0 Тогда
Возврат;
КонецЕсли;
НачатьТранзакцию(); // Используем транзакцию для надежности
Попытка
Для Каждого СсылкаИзСписка Из СписокВыделенных Цикл
// Проверяем тип, так как в списке могут быть группы или пустые значения
Если ТипЗнч(СсылкаИзСписка) = Тип("ДокументСсылка.ЗаявкаНаРасходДС") Тогда
ОбъектДок = СсылкаИзСписка.ПолучитьОбъект();
ОбъектДок.ПриоритетПлатежа = Перечисления.Приоритеты.Высокий;
ОбъектДок.Записать(РежимЗаписиДокумента.Запись);
КонецЕсли;
КонецЦикла;
ЗафиксироватьТранзакцию();
Сообщить("Выбранные документы успешно обновлены.");
Исключение
ОтменитьТранзакцию();
Сообщить("Произошла ошибка при массовом изменении: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Обратите внимание на использование методов НачатьТранзакцию() и ЗафиксироватьТранзакцию(). При работе с массивом данных это критически важно: если при записи десятого документа из ста произойдет ошибка, транзакция позволит откатить все изменения, сохранив целостность данных, либо вы сможете корректно обработать прерывание процесса.
Если ваша задача — техническое изменение поля (например, установка пометки или служебного признака), и вы не хотите, чтобы при этом срабатывали сложные проверки в модуле документа (процедуры ПередЗаписью или ПриЗаписи), можно использовать свойство ОбменДанными.Загрузка.
Рассмотрим, как это ускоряет процесс:
ОбъектДок = Ссылка.ПолучитьОбъект();
ОбъектДок.ПриоритетПлатежа = НужноеЗначение;
// Устанавливаем режим загрузки, чтобы пропустить тяжелые проверки
ОбъектДок.ОбменДанными.Загрузка = Истина;
ОбъектДок.Записать();
Важно: используйте этот метод с осторожностью. Если логика проведения документа жестко завязана на реквизит, который вы меняете, использование Загрузка = Истина может привести к рассогласованию данных в учете, так как не обновятся связанные регистры.
После того как мы программно изменили данные в базе, форма списка может не сразу отобразить новые значения. Чтобы "заставить" форму перечитать данные, выполним обновление. Рассмотрим два способа:
Обновить() у элемента формы. Это самый простой способ, который перерисовывает текущий список.ОповеститьОбИзменении(Ссылка). Это более "правильный" метод с точки зрения платформы 1С. Он сообщает всем открытым формам (включая списки и журналы), что объект с данной ссылкой изменился, и их нужно обновить.Пример вызова оповещения:
ОповеститьОбИзменении(ОбъектДок.Ссылка);
Если вы хотите разрешить пользователю менять приоритет прямо в ячейке списка, не нажимая на кнопки, выполним следующие настройки:
ТолькоЧтение снят.ПриоритетПлатежа, в её свойствах также снимем флаг ТолькоЧтение.РежимРедактирования в значение Вход или ВходПриВводе.ПриИзменении для элемента управления в этой колонке. В этом обработчике нужно прописать код записи объекта, аналогичный приведенному выше. Без этого кода изменение останется только "на экране" и не попадет в базу данных.Разберем по шагам создание такого обработчика:
Процедура СписокПриоритетПриИзменении(Элемент)
ТекДанные = ЭлементыФормы.Список.ТекущиеДанные;
Если ТекДанные = Неопределено Тогда
Возврат;
КонецЕсли;
// Элемент.Значение содержит то, что пользователь только что выбрал в ячейке
НовоеЗначение = Элемент.Значение;
ДокОбъект = ТекДанные.Ссылка.ПолучитьОбъект();
ДокОбъект.ПриоритетПлатежа = НовоеЗначение;
ДокОбъект.Записать();
КонецПроцедуры
Таким образом, мы рассмотрели все основные способы взаимодействия с данными документов из формы списка. Мы научились обходить ограничения обычных форм, правильно работать с объектами через ссылки, выполнять массовую обработку выделенных строк и обеспечивать актуальность данных на экране пользователя. Используя данные приемы, вы сможете создавать более интерактивные и удобные интерфейсы для конечных пользователей системы 1С.