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

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

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

Постановка задачи и базовый алгоритм решения

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

Для решения этой задачи необходимо выполнить следующие действия:

  1. Получить ссылку на документ, который нужно изменить.
  2. Преобразовать ссылку в объект для возможности редактирования (метод ПолучитьОбъект()).
  3. Найти нужные строки в табличной части объекта.
  4. Изменить значения реквизитов в этих строках.
  5. Записать измененный объект в базу данных.

Реализация через получение объекта и перебор строк

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

Рассмотрим пример кода:


НакладнаяОбъект = Настройки.Ссылка.ПолучитьОбъект();

Если ТаблицаТоваров.Количество() = 0 Тогда
    Для Каждого СтрокаТЧ Из НакладнаяОбъект.ДокументыРейса Цикл
        // Сравниваем контрагента в строке с выбранным в обработке
        Если СтрокаТЧ.Контрагент = Настройки.Контрагент Тогда                                                        
            СтрокаТЧ.Отсканирован = Истина;
        КонецЕсли;
    КонецЦикла;
    
    // Записываем изменения один раз после завершения цикла
    НакладнаяОбъект.Записать(РежимЗаписиДокумента.Запись);
    ЭтаФорма.Закрыть();
Иначе
    Сообщить("Не все товары отсканированы!");
КонецЕсли;

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

Оптимизация поиска строк с помощью метода НайтиСтроки

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

Проанализируем, как переписать наш код с использованием структуры отбора:


НакладнаяОбъект = Настройки.Ссылка.ПолучитьОбъект();

// Формируем структуру для поиска
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("Контрагент", Настройки.Контрагент);

// Получаем массив найденных строк
НайденныеСтроки = НакладнаяОбъект.ДокументыРейса.НайтиСтроки(ПараметрыОтбора);

Если НайденныеСтроки.Количество() > 0 Тогда
    Для Каждого Стр Из НайденныеСтроки Цикл
        Стр.Отсканирован = Истина;
    КонецЦикла;
    
    НакладнаяОбъект.Записать();
КонецЕсли;

Типичные ошибки при работе с объектами

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

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

Безопасная запись документа (Попытка-Исключение)

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

Применим блок Попытка...Исключение:


Попытка
    // Пытаемся записать объект в режиме обычной записи
    НакладнаяОбъект.Записать(РежимЗаписиДокумента.Запись);
Исключение
    ТекстОшибки = КраткоеОписаниеОшибки(ИнформацияОбОшибке());
    Сообщить("Не удалось обновить статус в документе! Ошибка: " + ТекстОшибки);
КонецПопытки;

Особенности работы в Управляемом приложении

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

Выясним, как должна выглядеть структура вызова в управляемом приложении:

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

Если же форма обработки открыта "поверх" формы документа, иногда эффективнее не записывать объект в базу напрямую, а передать данные обратно в форму документа через механизм Оповещение, чтобы пользователь сам нажал кнопку "Провести" и увидел изменения на экране.

Резюме

Для записи реквизита табличной части из обработки мы всегда используем переход от Ссылка к Объект. Важно помнить об оптимизации: минимизировать количество вызовов метода Записать(), использовать НайтиСтроки() для поиска и всегда проверять успешность выполнения операции. Такой подход гарантирует стабильную работу системы и целостность данных в вашей базе 1С.

← На главную