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

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

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

Постановка задачи и анализ структуры данных

Представим ситуацию: у нас есть документ с табличной частью ТЧ1, содержащей колонки Количество, Срок и Цена. Нам необходимо записать эти данные в регистр сведений, где измерением является Договор, а ресурсами выступают Количество, Срок1, Цена1, Срок2, Цена2, Срок3, Цена3, Срок4, Цена4. При этом, если в табличной части несколько строк имеют одинаковое значение Количество, они должны объединяться в одну запись регистра, распределяясь по порядковым номерам ресурсов.

Проанализируем пример данных. Если в ТЧ есть строки:

  1. Количество: 100, Срок: 1, Цена: 800
  2. Количество: 100, Срок: 30, Цена: 400

В регистр должна попасть одна запись: Количество = 100, Срок1 = 1, Цена1 = 800, Срок2 = 30, Цена2 = 400. Остальные ресурсы (3 и 4) остаются пустыми (или равными нулю).

Решение 1. Обход коллекции с использованием счетчика (Алгоритмический метод)

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

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


// 1. Выгружаем ТЧ и подготавливаем данные
ТаблицаДанных = ТЧ1.Выгрузить();
ТаблицаДанных.Сортировать("Количество"); // Группируем одинаковые количества рядом

// 2. Создаем набор записей регистра
НаборЗаписей = РегистрыСведений.НашРегистр.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Ссылка);

ТекущееКоличество = -1;
НоваяЗапись = Неопределено;
НомерРесурса = 1;

// 3. Обходим таблицу и распределяем данные
Для Каждого СтрокаТЧ Из ТаблицаДанных Цикл
    
    Если СтрокаТЧ.Количество <> ТекущееКоличество Тогда
        // Началась новая группа — создаем новую запись в регистре
        НоваяЗапись = НаборЗаписей.Добавить();
        НоваяЗапись.Договор = Ссылка.Договор;
        НоваяЗапись.Количество = СтрокаТЧ.Количество;
        ТекущееКоличество = СтрокаТЧ.Количество;
        НомерРесурса = 1;
    КонецЕсли;
    
    // Заполняем ресурсы по индексу (не более 4-х по условию задачи)
    Если НомерРесурса <= 4 Тогда
        НоваяЗапись["Срок" + НомерРесурса] = СтрокаТЧ.Срок;
        НоваяЗапись["Цена" + НомерРесурса] = СтрокаТЧ.Цена;
        НомерРесурса = НомерРесурса + 1;
    КонецЕсли;
    
КонецЦикла;

НаборЗаписей.Записать();

Важный момент: Обратите внимание на использование синтаксиса НоваяЗапись["ИмяРесурса" + Индекс]. Это позволяет обращаться к свойствам объекта программно, избегая громоздких конструкций Если...ИначеЕсли. Это делает код более лаконичным и читаемым.

Решение 2. Реализация через запрос (Метод Pivot)

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

Проанализируем ситуацию: нам нужно пронумеровать строки внутри каждой группы Количество. В запросе это можно сделать через соединение таблицы самой с собой.


ВЫБРАТЬ
    ТЧ.Количество КАК Количество,
    ТЧ.Срок КАК Срок,
    ТЧ.Цена КАК Цена,
    АВТОНОМЕРЗАПИСИ() КАК ИД // Если доступно в версии платформы, иначе через временные таблицы
ПОМЕСТИТЬ ВТ_Данные
ИЗ
    &ТЧ КАК ТЧ;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Т1.Количество,
    Т1.Срок,
    Т1.Цена,
    КОЛИЧЕСТВО(Т2.Цена) КАК НомерСтрокиГруппы
ПОМЕСТИТЬ ВТ_Пронумерованная
ИЗ
    ВТ_Данные КАК Т1
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Данные КАК Т2
        ПО Т1.Количество = Т2.Количество
            И Т1.ИД >= Т2.ИД
СГРУППИРОВАТЬ ПО
    Т1.Количество, Т1.Срок, Т1.Цена;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Количество,
    МАКСИМУМ(ВЫБОР КОГДА НомерСтрокиГруппы = 1 ТОГДА Срок ИНАЧЕ 0 КОНЕЦ) КАК Срок1,
    МАКСИМУМ(ВЫБОР КОГДА НомерСтрокиГруппы = 1 ТОГДА Цена ИНАЧЕ 0 КОНЕЦ) КАК Цена1,
    МАКСИМУМ(ВЫБОР КОГДА НомерСтрокиГруппы = 2 ТОГДА Срок ИНАЧЕ 0 КОНЕЦ) КАК Срок2,
    МАКСИМУМ(ВЫБОР КОГДА НомерСтрокиГруппы = 2 ТОГДА Цена ИНАЧЕ 0 КОНЕЦ) КАК Цена2
    // и так далее для 3 и 4
ИЗ
    ВТ_Пронумерованная
СГРУППИРОВАТЬ ПО
    Количество

Этот метод позволяет получить готовую «плоскую» таблицу, которую можно одной командой Загрузить() отправить в набор записей регистра. Это значительно быстрее при больших объемах данных, так как основная нагрузка ложится на СУБД.

Обработка исключительных ситуаций

Выясним причину возможных ошибок при выполнении данной задачи. Одной из главных проблем является ситуация, когда количество строк в группе превышает количество предусмотренных ресурсов (в нашем примере — более 4).

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

  1. Игнорирование лишних строк: Мы просто ограничиваем счетчик НомерРесурса <= 4. Это самый простой, но опасный путь, так как данные могут быть потеряны без уведомления пользователя.
  2. Создание «пакетов» записей: Если строк больше 4, мы должны создать следующую запись в регистре с тем же значением Количество. Однако здесь возникает проблема уникальности ключа записи. Если в регистре измерение только Договор и Количество, то вторая запись просто перезапишет первую.

Для решения проблемы уникальности рекомендуется добавить в регистр сведений дополнительное измерение — НомерПакета или КлючСвязи. Это позволит хранить неограниченное количество групп ресурсов для одного и того же договора.

Архитектурные рекомендации и Best Practice

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

Правильный подход (Best Practice): Вместо раздувания количества ресурсов, лучше добавить в регистр одно измерение ПорядковыйНомер (или Срок) и один набор ресурсов (Цена, Срок). В таком случае каждая строка ТЧ превратится в полноценную отдельную запись регистра. Разворачивание этих записей в «горизонтальный» вид для отчетов — это задача уровня представления (запроса или СКД), а не уровня хранения данных.

Если же архитектура системы уже задана и изменению не подлежит (как часто бывает в реальных проектах), используйте предложенный алгоритм со счетчиком и программным обращением к свойствам через квадратные скобки []. Это обеспечит максимальную стабильность и простоту поддержки вашего кода.

← На главную