При работе с платформой 1С 8.3 в контексте регламентных заданий или общих модулей часто возникает необходимость использовать структурированные данные, аналогичные табличным частям документов, для выполнения промежуточных расчетов. При этом крайне важно, чтобы эти данные не сохранялись в информационной базе и не требовали трудоемкого программного описания всех полей. Рассмотрим подробно различные подходы к решению этой задачи, проанализируем их преимущества и возможные недостатки.
Основная проблема, с которой мы сталкиваемся, заключается в желании использовать структуру существующей табличной части документа (например, Товары из РасходнаяНакладная) без необходимости вручную описывать каждую колонку с ее типом и свойствами. Это особенно актуально, если табличная часть содержит множество полей или ее структура может измениться в будущем.
Изначально автор предложил довольно оригинальное решение: создать полноценный объект документа, заполнить его табличную часть необходимыми данными для расчетов, выполнить вычисления, а затем просто не записывать этот документ в базу данных. Такой подход привлекателен своей простотой, поскольку позволяет использовать уже существующую структуру табличной части без дополнительного кодирования.
Давайте проанализируем этот подход с точки зрения его корректности и влияния на систему:
Пример кода (концептуальный):
// В общем модуле, используемом регламентным заданием
Функция ВыполнитьРасчетыСВременнойТЧ()
// Создаем новый объект документа в памяти
Док = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
// Получаем табличную часть для заполнения
ВременнаяТЧ = Док.Товары;
// Заполняем ВременнаяТЧ необходимыми данными
// Например, добавим несколько строк
НоваяСтрока = ВременнаяТЧ.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Товар 1");
НоваяСтрока.Количество = 10;
НоваяСтрока.Цена = 100;
НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;
НоваяСтрока = ВременнаяТЧ.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Товар 2");
НоваяСтрока.Количество = 5;
НоваяСтрока.Цена = 250;
НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;
// Выполняем необходимые расчеты, используя данные из ВременнаяТЧ
ОбщаяСумма = 0;
Для Каждого СтрокаТЧ Из ВременнаяТЧ Цикл
ОбщаяСумма = ОбщаяСумма + СтрокаТЧ.Сумма;
КонецЦикла;
Сообщить("Общая сумма товаров для расчета: " + ОбщаяСумма);
// Док не записывается в базу данных.
// После завершения выполнения функции, Док и ВременнаяТЧ будут автоматически удалены из памяти.
Возврат ОбщаяСумма;
КонецФункции;
Вывод по подходу: Хотя этот метод технически работоспособен и не приводит к проблемам с базой данных, он может быть менее явным и понятным для других разработчиков. С точки зрения "чистоты" кода и архитектуры, обычно предпочтительнее использовать объекты, специально предназначенные для временного хранения данных.
ТаблицаЗначений является наиболее распространенным и явно предназначенным для работы с временными табличными данными в 1С. Этот программный объект существует исключительно в оперативной памяти и позволяет программно создавать произвольные наборы данных.
Разберем основные принципы и способы использования ТаблицыЗначений:
ТаблицаЗначений – это коллекция, похожая на таблицу в базе данных, но существующая только в памяти. Вы можете описывать колонки (указывая их имя, тип и заголовок), добавлять, редактировать, удалять и сортировать строки. Она не привязана напрямую к метаданным конфигурации (за исключением типов колонок) и не взаимодействует с базой данных. Память, занимаемая ТаблицейЗначений, также автоматически освобождается системой 1С.Выгрузить() от пустой ссылки на табличную часть.Рассмотрим примеры:
Пример 1: Получение структуры ТЗ из пустой ссылки на табличную часть.
Это очень удобный способ, когда мы хотим получить структуру конкретной табличной части документа (например, Товары из Документы.РеализацияТоваровУслуг) без необходимости создавать сам документ.
// В общем модуле
Функция ПолучитьВременнуюТЧКакТЗ_ПустаяСсылка()
// Создаем пустую ссылку на документ "РеализацияТоваровУслуг"
ПустаяСсылкаДокумента = Документы.РеализацияТоваровУслуг.ПустаяСсылка();
// Получаем табличную часть "Товары" из этой пустой ссылки
// и выгружаем ее структуру (с пустыми данными) в ТаблицуЗначений
ВременнаяТЧ_КакТЗ = ПустаяСсылкаДокумента.Товары.Выгрузить();
Сообщить("Получена ТаблицаЗначений со структурой ТЧ Товары. Количество колонок: " + ВременнаяТЧ_КакТЗ.Колонки.Количество());
// Далее ВременнаяТЧ_КакТЗ можно заполнять и использовать для расчетов
НоваяСтрока = ВременнаяТЧ_КакТЗ.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Тестовый Товар");
НоваяСтрока.Количество = 7;
НоваяСтрока.Цена = 120;
// ... и так далее для других колонок
Возврат ВременнаяТЧ_КакТЗ;
КонецФункции;
Пример 2: Получение структуры и данных ТЗ из запроса.
Если данные для временной табличной части нужно получать из базы данных, или ее структуру удобно определить через запрос, мы можем использовать запрос для формирования ТаблицыЗначений.
// В общем модуле
Функция ПолучитьВременнуюТЧКакТЗ_ИзЗапроса()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
| РеализацияТоваровУслугТовары.Количество КАК Количество,
| РеализацияТоваровУслугТовары.Цена КАК Цена,
| РеализацияТоваровУслугТовары.Сумма КАК Сумма
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
|ГДЕ
| ИСТИНА // Здесь может быть любое условие выборки, или мы просто выбираем структуру
|
|УПОРЯДОЧИТЬ ПО
| Номенклатура
|";
// Если нам нужна только структура, но без данных, можно добавить ЛОЖНОЕ условие
// Запрос.Текст = Запрос.Текст + " ГДЕ ЛОЖЬ";
// Выполняем запрос и выгружаем результат в ТаблицуЗначений
ВременнаяТЧ_КакТЗ = Запрос.Выполнить().Выгрузить();
Сообщить("Получена ТаблицаЗначений из запроса. Количество строк: " + ВременнаяТЧ_КакТЗ.Количество());
Возврат ВременнаяТЧ_КакТЗ;
КонецФункции;
Обратите внимание, что если нам нужна только структура, но без данных, мы можем добавить к запросу условие ГДЕ ЛОЖЬ. В этом случае запрос вернет пустой результат, но с определенной структурой колонок.
Один из самых элегантных способов, предложенных в обсуждении, позволяет создать объект типа ТабличнаяЧасть напрямую, без создания родительского объекта документа. Этот подход идеален, когда вам нужна коллекция, которая по структуре точно соответствует определенной табличной части документа из метаданных.
Разберем этот метод:
Новый() с полным именем типа "ДокументТабличнаяЧасть.<ИмяДокумента>.<ИмяТабличнойЧасти>". Созданный объект будет являться коллекцией строк, каждая из которых имеет поля, соответствующие колонкам определенной табличной части. Этот объект существует независимо от объекта-владельца (документа) в оперативной памяти.Пример кода:
// В общем модуле
Функция ПолучитьВременнуюТЧНапрямую()
// Создаем объект ТабличнаяЧасть, соответствующий структуре ТЧ Товары
// документа "РасходнаяНакладная" (или "РеализацияТоваровУслуг" в УТ 10.3)
Таблица = Новый ("ДокументТабличнаяЧасть.РеализацияТоваровУслуг.Товары");
Сообщить("Создан объект типа ДокументТабличнаяЧасть. Количество строк (пока 0): " + Таблица.Количество());
// Теперь Таблица готова к заполнению, как любая другая табличная часть
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Продукт А");
НоваяСтрока.Количество = 20;
НоваяСтрока.Цена = 50;
НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию("Продукт Б");
НоваяСтрова.Количество = 15;
НоваяСтрока.Цена = 90;
НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;
// Выполняем расчеты
СуммаПоТЧ = 0;
Для Каждого СтрТЧ Из Таблица Цикл
СуммаПоТЧ = СуммаПоТЧ + СтрТЧ.Сумма;
КонецЦикла;
Сообщить("Сумма по временной табличной части: " + СуммаПоТЧ);
Возврат Таблица;
КонецФункции;
Сравнение с ТаблицейЗначений:
ТаблицаЗначений более универсальна, ее структуру можно определить программно без привязки к существующим метаданным, или получить из произвольного запроса.ДокументТабличнаяЧасть удобен, когда вам *точно* нужна структура конкретной табличной части из конфигурации и вы не хотите создавать полный объект документа или писать запросы для получения структуры. Он обеспечивает строгую типизацию полей в соответствии с метаданными.Для более сложных сценариев, особенно когда требуется обработка больших объемов данных или выполнение многоступенчатых операций с данными прямо внутри запроса, мы можем использовать временные таблицы в языке запросов 1С.
Рассмотрим, как это работает:
TempDB). После завершения выполнения запроса эти временные таблицы автоматически удаляются.Пример использования временных таблиц в запросе:
// В общем модуле
Функция ВыполнитьРасчетыСВременнымиТаблицамиЗапроса()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыНаСкладах.Номенклатура КАК Номенклатура,
| ТоварыНаСкладах.ВНаличииОстаток КАК Количество
|ПОМЕСТИТЬ ВремТабОстатки
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладах
|ГДЕ
| ТоварыНаСкладах.Склад = &Склад
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВремТабОстатки.Номенклатура КАК Номенклатура,
| ВремТабОстатки.Количество КАК Количество,
| ЦеныНоменклатурыСрезПоследних.Цена КАК Цена,
| ВремТабОстатки.Количество * ЦеныНоменклатурыСрезПоследних.Цена КАК Сумма
|ИЗ
| ВремТабОстатки КАК ВремТабОстатки
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК ЦеныНоменклатурыСрезПоследних
| ПО ВремТабОстатки.Номенклатура = ЦеныНоменклатурыСрезПоследних.Номенклатура
|ГДЕ
| ЦеныНоменклатурыСрезПоследних.ТипЦен = &ТипЦен
|;
|
|УНИЧТОЖИТЬ ВремТабОстатки
|";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Склад", Справочники.Склады.НайтиПоНаименованию("Основной склад"));
Запрос.УстановитьПараметр("ТипЦен", Справочники.ТипыЦенНоменклатуры.НайтиПоНаименованию("Розничная"));
// Выполняем запрос
РезультатЗапроса = Запрос.Выполнить();
// Выгружаем результат в ТаблицуЗначений для дальнейшей обработки в коде
ИтоговаяТаблица = РезультатЗапроса.Выгрузить();
ОбщаяСуммаПоОстаткам = ИтоговаяТаблица.Итог("Сумма");
Сообщить("Общая сумма товаров на складе по розничным ценам: " + ОбщаяСуммаПоОстаткам);
Возврат ИтоговаяТаблица;
КонецФункции;
В этом примере мы сначала получаем остатки товаров на складе во временную таблицу ВремТабОстатки, а затем вторым запросом соединяем эти остатки с ценами, вычисляем сумму и получаем окончательный результат.
Этот механизм, хотя и не предназначен напрямую для создания "временных табличных частей" для расчетов, может быть полезен, если результаты наших вычислений, представленные в виде ТаблицыЗначений или другого объекта, необходимо передать между различными сеансами, например, из фонового (регламентного) задания в клиентский сеанс, который его инициировал.
Рассмотрим его особенности:
ТаблицуЗначений) во временное хранилище и получаете уникальный адрес этого значения. По этому адресу вы можете затем получить сохраненные данные из другого сеанса или в другом контексте выполнения. Время жизни данных во временном хранилище может быть контролируемым, например, привязанным к форме или имеющим определенный период истечения.Пример использования временного хранилища (концептуальный):
// В модуле регламентного задания
Функция ВыполнитьДлительныеРасчетыИПередатьРезультат()
// Предположим, мы получили некую ТаблицуЗначений с результатами расчетов
ВременнаяТЧ_КакТЗ = Новый ТаблицаЗначений;
ВременнаяТЧ_КакТЗ.Колонки.Добавить("Код");
ВременнаяТЧ_КакТЗ.Колонки.Добавить("Описание");
// ... заполняем ТЗ данными
// Помещаем ТаблицуЗначений во временное хранилище
АдресВременногоХранилища = ПоместитьВоВременноеХранилище(ВременнаяТЧ_КакТЗ, УникальныйИдентификатор);
// УникальныйИдентификатор может быть передан обратно клиенту или записан в регистр
Возврат АдресВременногоХранилища;
КонецФункции;
// В клиентском или серверном модуле, который должен получить результат
// Например, после завершения фонового задания
Функция ПолучитьРезультатИзХранилища(АдресВХ)
// Получаем ТаблицуЗначений по адресу из временного хранилища
ПолученнаяТЗ = ПолучитьИзВременногоХранилища(АдресВХ);
Если ПолученнаяТЗ <> Неопределено Тогда
Сообщить("Результаты расчетов получены из временного хранилища. Количество строк: " + ПолученнаяТЗ.Количество());
// ... дальнейшая работа с полученной ТЗ
Иначе
Сообщить("Данные по адресу " + АдресВХ + " не найдены или устарели.");
КонецЕсли;
КонецФункции;
Этот механизм обеспечивает гибкость при обмене большими объемами данных между разными контекстами выполнения, но для локальных расчетов в рамках одного вызова общего модуля обычно не требуется.
Мы рассмотрели несколько подходов к работе с временными табличными данными в 1С 8.3, каждый из которых имеет свои особенности и области применения:
ТаблицеЗначений в этом конкретном случае.Для решения задачи, описанной в исходном сообщении (расчеты в общем модуле, использование структуры ТЧ документа, без записи), предпочтительными и наиболее "чистыми" являются подходы с использованием ТаблицаЗначений или прямого создания объекта ДокументТабличнаяЧасть. Они обеспечивают необходимую гибкость, не взаимодействуют с базой данных и явно демонстрируют временный характер данных.