Как программно изменить значение реквизита в текущей строке таблицы в 1С?

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

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

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

1. Почему не работал первоначальный код? Разбор типичной ошибки

Рассмотрим фрагмент кода, с которого началось обсуждение на форуме:


Для Каждого ТекСтрока из Товары Цикл 
КонецЦикла;
ТекСтрока.Количество = МассивСтрок[0].Количество;

Выясним причину, по которой данный код не выполнял требуемое действие. Здесь разработчик объявил цикл Для Каждого, однако тело этого цикла (пространство между ключевыми словами Цикл и КонецЦикла) осталось пустым. Программа мгновенно обходит все строки коллекции Товары, не производя с ними никаких действий.

После завершения цикла переменная ТекСтрока в зависимости от платформы либо указывает на самую последнюю строку таблицы, либо принимает значение Неопределено. Она физически не указывает на ту активную строку, которую выбрал пользователь на экране. Попытка присвоить значение переменной вне цикла не дает нужного результата и может привести к ошибке.

2. Правильная работа с текущей строкой в Управляемых формах

В управляемом приложении работа с данными разделена на клиентскую и серверную части. Рассмотрим, как правильно изменять текущую строку в обоих случаях.

Изменение данных на Клиенте (&НаКлиенте)

Если вам нужно изменить значение на клиенте, самым простым и быстрым способом является использование свойства ТекущиеДанные таблицы формы. Данное свойство возвращает строку, с которой в данный момент взаимодействует пользователь.

Разберем пример безопасного изменения количества на клиенте:


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

Изменение данных на Сервере (&НаСервере) и опасность использования НомерСтроки - 1

Часто в коде на форумах можно встретить такое предложение для работы на сервере:


Объект.МояТабличнаяЧасть[ИндексСтроки].Количество = 25;
// Где ИндексСтроки получается как ТекущиеДанные.НомерСтроки - 1

Проанализируем, почему этот способ является крайне опасным. Если пользователь на форме отсортировал таблицу по любой колонке, установил отбор (фильтрацию) или если ранее из таблицы удалялись строки, визуальный порядок на клиенте перестанет совпадать с физическим порядком строк в коллекции Объект.Товары на сервере. В результате ваш код изменит данные совершенно в другой строке!

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

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


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

&НаСервере
Процедура ИзменитьКоличествоНаСервере(ИдентификаторСтроки)
    // Находим точную строку в данных объекта по её идентификатору
    СтрокаТаблицы = Объект.Товары.НайтиПоИдентификатору(ИдентификаторСтроки);
    Если СтрокаТаблицы <> Неопределено Тогда
        СтрокаТаблицы.Количество = 25; // Безопасное изменение
    КонецЕсли;
КонецПроцедуры

3. Нюансы безопасности в Обычных формах

Для обычных форм решение выглядит иначе. В конце обсуждения автор привел следующий пример кода:


Переменная = ЭлементыФормы.Товары.ТекущаяСтрока;
Переменная.Количество = 22;

Этот код будет работать только при условии, что в таблице есть строки и одна из них выделена. Если же таблица пуста, свойство ТекущаяСтрока вернет значение Неопределено. Попытка обратиться к свойству Количество вызовет критическую ошибку «Значение не является значением объектного типа» и приведет к аварийному завершению сеанса пользователя.

Чтобы избежать сбоев, всегда добавляйте проверку:


ТекущаяСтрока = ЭлементыФормы.Товары.ТекущаяСтрока;
Если ТекущаяСтрока <> Неопределено Тогда
    ТекущаяСтрока.Количество = 22;
КонецЕсли;

4. Оптимизация производительности: Безконтекстные серверные вызовы

Контекстный вызов сервера (&НаСервере) для изменения всего одного реквизита неоптимален, так как на сервер и обратно передается весь контекст формы (все данные элементов управления и таблиц). При медленном интернет-соединении это приводит к задержкам интерфейса.

Если для изменения количества вам требуется получить какие-то данные из базы данных (например, узнать актуальную цену или остаток товара), используйте безконтекстный серверный вызов &НаСервереБезКонтекста.

Рассмотрим пример правильной реализации:


&НаКлиенте
Процедура ПолучитьИУстановитьЦенуТовара()
    ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
    Если ТекущиеДанные <> Неопределено Тогда
        // Передаем на сервер только ссылку на номенклатуру (минимум трафика)
        АктуальнаяЦена = ПолучитьЦенуНаСервереБезКонтекста(ТекущиеДанные.Номенклатура);
        // Присваиваем цену напрямую на клиенте
        ТекущиеДанные.Цена = АктуальнаяЦена;
        ТекущиеДанные.Сумма = ТекущиеДанные.Количество * АктуальнаяЦена;
    КонецЕсли;
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьЦенуНаСервереБезКонтекста(СсылкаНоменклатура)
    // Быстрый запрос к базе без передачи всей формы
    Возврат Ценообразование.ПолучитьАктуальнуюЦену(СсылкаНоменклатура);
КонецФункции

5. Автоматический пересчет связанных полей (Сумма, НДС)

При программном изменении реквизита на клиенте через ТекущиеДанные.Количество = 25; другие колонки (например, «Сумма», которая должна рассчитываться как Количество * Цена) автоматически не пересчитаются. Платформа запускает обработчики событий (такие как ПриИзменении) только при интерактивном вводе данных пользователем.

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


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

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

← На главную