Как получить значение строки табличной части документа-основания в реализации товаров?

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

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

Разберем основные сложности этой задачи, выясним причину возможных ошибок и рассмотрим правильные варианты реализации с примерами кода.

Особенности связи строк реализации и заказа покупателя

Прежде чем приступать к написанию кода, проанализируем архитектуру данных платформы «1С:Предприятие» в типовых решениях:

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

Вариант 1. Пакетный запрос с временной таблицей (рекомендуемый способ)

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

Разберем по шагам, как это реализовать:

  1. Выгружаем табличную часть Товары в таблицу значений методом Выгрузить().
  2. Добавляем поле с номером строки (или индексом), чтобы гарантированно сопоставить результат с исходными данными.
  3. Помещаем таблицу в менеджер временных таблиц запроса и соединяем её с реальной таблицей базы данных Документ.ЗаказПокупателя.Товары по ключевым полям.

Посмотрим на пример программного кода:


// 1. Выгружаем текущую табличную часть незаписанного документа
ТаблицаТоваров = Товары.Выгрузить();

// Добавим колонку для точной идентификации строк при необходимости
Если ТаблицаТоваров.Колонки.Найти("НомерСтрокиТекущий") = Неопределено Тогда
    ТаблицаТоваров.Колонки.Добавить("НомерСтрокиТекущий", Новый ОписаниеТипов("Число"));
    Для Каждого Стр Из ТаблицаТоваров Цикл
        Стр.НомерСтрокиТекущий = ТаблицаТоваров.Индекс(Стр) + 1;
    КонецЦикла;
КонецЕсли;

// 2. Формируем запрос
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.УстановитьПараметр("ТаблицаТоваров", ТаблицаТоваров);

Запрос.Текст = 
"ВЫБРАТЬ
|   ТТ.НомерСтрокиТекущий КАК НомерСтрокиТекущий,
|   ТТ.Номенклатура КАК Номенклатура,
|   ТТ.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|   ТТ.ЗаказПокупателя КАК ЗаказПокупателя
|ПОМЕСТИТЬ ВТ_ТоварыРеализации
|ИЗ
|   &ТаблицаТоваров КАК ТТ
|ГДЕ
|   ТТ.ЗаказПокупателя <> ЗНАЧЕНИЕ(Документ.ЗаказПокупателя.ПустаяСсылка)
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|   ВТ.НомерСтрокиТекущий КАК НомерСтрокиТекущий,
|   ВТ.ЗаказПокупателя КАК ЗаказПокупателя,
|   ВТ.Номенклатура КАК Номенклатура,
|   ЗаказТовары.ДатаОтгрузки КАК ДатаОтгрузкиИзЗаказа,
|   ЗаказТовары.Размещение КАК РазмещениеИзЗаказа
|ИЗ
|   ВТ_ТоварыРеализации КАК ВТ
|   ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя.Товары КАК ЗаказТовары
|       ПО ВТ.ЗаказПокупателя = ЗаказТовары.Ссылка
|       И ВТ.Номенклатура = ЗаказТовары.Номенклатура
|       И ВТ.ХарактеристикаНоменклатуры = ЗаказТовары.ХарактеристикаНоменклатуры";

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    // Выполняем проверку или расчет по полученным значениям
    Если ЗначениеЗаполнено(Выборка.ДатаОтгрузкиИзЗаказа) Тогда
        // Применяем условие к строке реализации
    КонецЕсли;
КонецЦикла;

Вариант 2. Точечный поиск строк в коде (для интерактивных событий формы)

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

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


// Получаем текущую строку табличной части
СтрокаРеализации = ЭлементыФормы.Товары.ТекущиеДанные;

Если СтрокаРеализации <> Неопределено И ЗначениеЗаполнено(СтрокаРеализации.ЗаказПокупателя) Тогда
    // Получаем табличную часть заказа через выгрузку
    ТаблицаТоваровЗаказа = СтрокаРеализации.ЗаказПокупателя.Товары.Выгрузить();
    
    // Формируем отбор по ключевым полям для поиска нужной строки
    ПараметрыОтбора = Новый Структура;
    ПараметрыОтбора.Вставить("Номенклатура", СтрокаРеализации.Номенклатура);
    
    Если СтрокаРеализации.ВладелецФормы().УчетПоХарактеристикам Тогда
        ПараметрыОтбора.Вставить("ХарактеристикаНоменклатуры", СтрокаРеализации.ХарактеристикаНоменклатуры);
    КонецЕсли;
    
    НайденныеСтроки = ТаблицаТоваровЗаказа.НайтиСтроки(ПараметрыОтбора);
    
    Если НайденныеСтроки.Количество() > 0 Тогда
        // Берем первую совпавшую строку заказа
        СтрокаЗаказа = НайденныеСтроки[0];
        ЗначениеРеквизита = СтрокаЗаказа.ПроцентАвтоматическихСкидок;
        
        // Выполняем условие
        Если ЗначениеРеквизита > 10 Тогда
            // Действие по условию
        КонецЕсли;
    КонецЕсли;
КонецЕсли;

Вариант 3. Использование процедуры ОбработкаЗаполнения

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

Откроем модуль объекта документа РеализацияТоваровУслуг и перейдем к процедуре ОбработкаЗаполнения(Основание). В параметре Основание передается ссылка на исходный документ ЗаказПокупателя. В этот момент мы имеем прямой доступ к его реквизитам и строкам:


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

Стоит отметить, что в типовой конфигурации УПП 1.3 при вводе реализации остатки номенклатуры к отгрузке рассчитываются по регистру накопления ЗаказыПокупателей. Если переносимые вами поля отсутствуют в регистре, их необходимо дозаполнять именно чтением табличной части заказа.

← На главную