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

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

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

Анализ структуры алгоритма переноса данных

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

Часто в коде обработки можно встретить обращение к свойствам текущей строки через объект верхнего уровня (например, СтрДерева), что приводит к некорректной работе формул для конкретных реквизитов. Проанализируем исправленный вариант кода, где мы обращаемся непосредственно к переменной цикла стрТч:


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

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

Проблема пересчета итогов при программном заполнении

Одной из главных проблем, с которой сталкиваются разработчики, является «застывание» итоговых сумм. При интерактивной работе пользователя (ввод данных руками в форму) срабатывают события ПриИзменении, которые автоматически рассчитывают сумму как произведение цены на количество. Однако при программном заполнении через метод Добавить() эти события не вызываются.

Проанализируем ситуацию: мы заполнили Количество и Цена, но колонка Сумма осталась нулевой. Как это исправить? Рассмотрим несколько подходов.

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

Методы записи и корректировки времени документа

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


// Установка даты со смещением (например, на 1 час назад от даты источника)
ДокПриемник.Дата = ДокИсточник.Дата - 3600; // 3600 секунд = 1 час

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

Обратим внимание на работу с датой. В 1С значения даты хранятся в секундах. Поэтому, если нам нужно изменить время документа (например, чтобы он шел строго перед документом-источником), мы вычитаем нужное количество секунд. В сообщениях форума упоминалась строка ДокИсточник.Дата - "00000000010000" — это альтернативный формат записи, но использование секунд (числовой тип) считается более прозрачным и надежным методом.

Особенности использования функции Выполнить()

Функция Выполнить() позволяет исполнять код, написанный пользователем прямо в пользовательском режиме. Однако разберем важные нюансы:

Во-первых, контекст переменных. Чтобы код внутри Выполнить() «видел» переменную Результат, она должна быть объявлена в той же локальной области видимости, что и сама функция. Мы создаем переменную Результат = ""; непосредственно перед вызовом.

Во-вторых, типизация данных. Если ваша формула возвращает число, а реквизит документа является строкой, платформа может выдать ошибку или записать пустую ссылку. Рекомендуем проверять типы или использовать явное приведение, например: СтрТчПриемник[ИмяРеквизита] = Число(Результат);.

Подведение итогов и рекомендации

Для создания надежного механизма переноса данных мы рекомендуем придерживаться следующего алгоритма:

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

← На главную