Как добавить значение в текущую строку табличной части документа в 1С:Предприятии 8.3 при работе на сервере?

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

При разработке в 1С:Предприятии 8.3 часто возникает необходимость программно заполнять или изменять данные в табличных частях документов. Одна из распространенных задач — добавить или изменить значение в "текущей" строке табличной части, но сделать это в серверной процедуре. Разберем эту задачу подробно, учитывая нюансы клиент-серверного взаимодействия.

Для начала, давайте проанализируем ситуацию. Когда мы говорим о "текущей строке" в контексте формы, мы обычно имеем в виду строку, которая выделена пользователем или с которой взаимодействует пользовательский интерфейс. Однако, когда выполнение кода переходит на сервер, понятие "текущей строки" как элемента формы теряет свой прямой смысл, поскольку сервер не имеет доступа к элементам пользовательского интерфейса клиента. Тем не менее, 1С предоставляет механизмы для идентификации и манипуляции нужной строкой на сервере.

Выделим два основных подхода к решению этой задачи:

  1. Изменение данных на клиенте после получения необходимой информации с сервера.
  2. Непосредственная модификация объекта документа на сервере, путем передачи идентификатора строки с клиента.

Мы рассмотрим оба варианта, их преимущества и недостатки, а также приведем подробные примеры кода.

Подход 1: Изменение данных на клиенте с получением информации с сервера (Функции &НаСервереБезКонтекста)

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

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

Рассмотрим подробный пример, когда при изменении поля "Код" в строке табличной части "Товары" мы хотим найти соответствующую номенклатуру и заполнить поле "Товар":


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

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

Преимущества этого подхода:

  1. Минимальный трафик: На сервер передается только необходимый минимум данных (в данном случае, код номенклатуры), и обратно возвращается только ссылка на найденный элемент.
  2. Производительность: Вызовы &НаСервереБезКонтекста обычно быстрее, так как не происходит полной инициализации контекста формы и объекта документа на сервере.
  3. Отложенное сохранение: Изменения вносятся в клиентские данные формы и будут сохранены в базе только при записи или проведении документа, что удобно для интерактивной работы пользователя.

Недостатки:

  1. Изменения только в форме: Этот метод изменяет только клиентское представление данных. Если вам нужно, чтобы изменения были немедленно зафиксированы в серверном объекте (например, для запуска какой-то серверной логики, которая работает непосредственно с объектом), то этот подход может быть недостаточен.
  2. Доступ к объекту: Из функции &НаСервереБезКонтекста нет прямого доступа к объекту документа формы (переменной Объект), к элементам формы и другим свойствам контекста формы.

Подход 2: Непосредственная модификация объекта на сервере (Процедуры &НаСервере)

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

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

Проанализируем шаги:

  1. На клиенте получаем ИдентификаторСтроки текущей строки табличной части. Это уникальный временный идентификатор, который сохраняется в течение сеанса работы формы.
  2. Вызываем серверную процедуру (&НаСервере), передавая ей этот ИдентификаторСтроки и любые другие необходимые данные (например, значение кода, которое было изменено).
  3. В серверной процедуре используем переменную Объект, которая является ссылкой на серверный экземпляр документа, открытого в форме.
  4. Находим нужную строку табличной части с помощью Объект.ИмяТабличнойЧасти.НайтиПоИдентификатору(ИдентификаторСтроки).
  5. Модифицируем свойства найденной строки серверного объекта.

Рассмотрим пример, который похож на предыдущий, но теперь все действия по поиску и присвоению происходят на сервере:


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

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

Важно отметить: после выполнения серверной процедуры УстановитьНоменклатуруПоКодуНаСервере, изменения, внесенные в СтрокаТЧ, автоматически синхронизируются с клиентским представлением формы. Это происходит потому, что переменная Объект на сервере — это тот же объект, который открыт в форме, и система управления формой отслеживает его изменения.

Преимущества этого подхода:

  1. Прямая модификация объекта: Изменения вносятся непосредственно в серверный экземпляр объекта документа. Это гарантирует, что все связанные события объекта (например, обработчики ПриИзменении в модуле объекта) будут корректно срабатывать.
  2. Полный контекст: В процедуре &НаСервере у вас есть полный доступ к переменной Объект, а значит, ко всем данным документа, его реквизитам и табличным частям, что позволяет реализовать сложную логику, зависящую от состояния всего документа.
  3. Целостность данных: Полезно, когда изменения в одной строке табличной части могут влиять на другие данные документа (например, расчет итогов по документу).

Недостатки:

  1. Больше трафика: При каждом вызове серверной процедуры &НаСервере происходит полная сериализация и десериализация контекста формы, что может быть более ресурсоемким, чем вызов &НаСервереБезКонтекста.
  2. Производительность: Частые вызовы &НаСервере могут негативно сказаться на производительности, особенно в медленных сетях. Старайтесь группировать логику в одном серверном вызове, если это возможно.

Важные аспекты клиент-серверного взаимодействия и ТекущаяСтрока

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

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

В то время как на клиенте мы можем использовать Элементы.ИмяТаблицы.ТекущиеДанные для непосредственного доступа к данным текущей строки как к объекту (например, Элементы.Товары.ТекущиеДанные.Код), на сервере такой прямой доступ к "текущим данным" формы отсутствует. Это означает, что для работы с данными на сервере нам нужно явно найти строку по переданному идентификатору.

Метод НайтиПоИдентификатору() является ключевым для работы с идентификаторами строк табличных частей на сервере. Он позволяет получить ссылку на объект строки табличной части по этому идентификатору. Если идентификатор не найден (например, строка была удалена или изменена), метод вернет Неопределено.

Различие между &НаСервере и &НаСервереБезКонтекста:

  1. &НаСервере: Процедура или функция выполняется на сервере в контексте текущей формы. Это означает, что она имеет доступ к переменной Объект (объекту документа, который открыт в форме), к элементам формы (через прокси-объект), а также ко всем свойствам и методам модуля формы. Каждый такой вызов передает состояние всей формы на сервер и обратно.
  2. &НаСервереБезКонтекста: Функция выполняется на сервере вне контекста формы. Она не имеет доступа к переменной Объект, элементам формы и другим свойствам контекста формы. Такие функции используются для выполнения "чистой" серверной логики, такой как запросы к базе данных, поиск элементов справочников, выполнение сложных расчетов, которые не зависят от состояния конкретной формы. Эти вызовы более легковесны.

Когда какой метод использовать:

Пример комплексного решения (заполнение номенклатуры по коду)

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

Мы реализуем это через подход 1 (&НаСервереБезКонтекста) для минимизации серверных вызовов и более отзывчивого интерфейса:


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

// В модуле формы документа или в общем модуле
&НаСервереБезКонтекста
Функция ПолучитьДанныеНоменклатурыПоКодуНаСервере(Код)
    // Создаем структуру для возврата нескольких значений
    Результат = Новый Структура();
    Результат.Вставить("Номенклатура", Справочники.Номенклатура.ПустаяСсылка());
    Результат.Вставить("ЕдиницаИзмерения", Справочники.ЕдиницыИзмерения.ПустаяСсылка());
    Результат.Вставить("Цена", 0);
    
    НайденнаяНоменклатура = Справочники.Номенклатура.НайтиПоРеквизиту("Код", Код);
    
    Если НайденнаяНоменклатура <> Неопределено Тогда
        Результат.Номенклатура       = НайденнаяНоменклатура;
        Результат.ЕдиницаИзмерения  = НайденнаяНоменклатура.ЕдиницаИзмерения; // Предполагаем, что в номенклатуре есть реквизит "ЕдиницаИзмерения"
        
        // Пример получения цены (может быть сложнее, например, из регистра сведений)
        // Для простоты возьмем цену из условного регистра "ЦеныНоменклатуры"
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ ПЕРВЫЕ 1
            |    ЦеныНоменклатурыСрезПоследних.Цена
            |ИЗ
            |    РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
            |        &Дата,
            |        Номенклатура = &Номенклатура
            |    ) КАК ЦеныНоменклатурыСрезПоследних";
        
        Запрос.УстановитьПараметр("Дата", ТекущаяДата());
        Запрос.УстановитьПараметр("Номенклатура", НайденнаяНоменклатура);
        
        Выборка = Запрос.Выполнить().Выбрать();
        Если Выборка.Следующий() Тогда
            Результат.Цена = Выборка.Цена;
        КонецЕсли;
    КонецЕсли;
    
    Возврат Результат;
КонецФункции

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

Заключение

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

  1. Для быстрого получения данных и обновления только клиентского представления формы, предпочтительно использовать клиентскую процедуру, которая вызывает &НаСервереБезКонтекста функцию. Этот метод минимизирует нагрузку и обеспечивает высокую отзывчивость.
  2. Для прямой модификации серверного объекта документа и выполнения логики, требующей полного контекста документа, используйте клиентскую процедуру, которая вызывает &НаСервере процедуру, передавая ей идентификатор текущей строки. Этот подход гарантирует целостность данных объекта и позволяет использовать сложную серверную логику.

Понимание различий между этими подходами и правильный их выбор значительно повысят эффективность и производительность ваших решений на платформе 1С:Предприятие 8.3.

← На главную