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

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

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

Способ 1. Использование запроса (Рекомендуемый метод)

Обращение к данным через запрос — это наиболее производительный и правильный способ с точки зрения платформенных механизмов. Давайте разберем, почему вариант прямого перебора всех строк табличной части (как в сообщении 5) является ошибочным и как его исправить.

Основная проблема кода из примера заключалась в том, что запрос выбирал все цены из всех существующих документов «Прайс-лист», а цикл возвращал цену первой попавшейся строки. Чтобы получить корректные данные, мы должны использовать параметры и условия.

Рассмотрим правильную реализацию функции получения цены на сервере:


&НаСервере
Функция ПолучитьЦенуНоменклатуры(АктуальнаяНоменклатура, СсылкаНаПрайс) Экспорт
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ПрайсЛистСостав.Цена КАК Цена
        |ИЗ
        |    Документ.ПрайсЛист.Состав КАК ПрайсЛистСостав
        |ГДЕ
        |    ПрайсЛистСостав.Ссылка = &СсылкаНаПрайс
        |    И ПрайсЛистСостав.ВидЛома = &Номенклатура";
    
    Запрос.УстановитьПараметр("СсылкаНаПрайс", СсылкаНаПрайс);
    Запрос.УстановитьПараметр("Номенклатура", АктуальнаяНоменклатура);
    
    РезультатЗапроса = Запрос.Выполнить();
    
    Если Не РезультатЗапроса.Пустой() Тогда
        Выборка = РезультатЗапроса.Выбрать();
        Выборка.Следующий();
        Возврат Выборка.Цена;
    КонецЕсли;
    
    Возврат 0;
    
КонецФункции

Важные замечания по коду:

  1. Использование условия ГДЕ позволяет базе данных моментально найти нужную строку, не сканируя лишние документы.
  2. Параметр &СсылкаНаПрайс критически важен, так как в системе может быть множество прайс-листов. Вы должны четко указать, из какого именно документа брать цену (например, из реквизита объекта или выбрав последний по дате).
  3. Мы проверяем результат на пустоту с помощью метода Пустой(), чтобы избежать ошибок, если номенклатура не найдена в прайсе.

Способ 2. Использование метода Найти() (Объектная техника)

Иногда, если у нас уже есть ссылка на документ в памяти, можно воспользоваться методом Найти() самой табличной части. Рассмотрим, как это работает на практике.

Предположим, у нас есть переменная ДокументОбъект, содержащая ссылку на нужный прайс-лист. Мы можем осуществить поиск следующим образом:


// Допустим, ПрайсСсылка — это ссылка на нужный документ ПрайсЛист
ПрайсОбъект = ПрайсСсылка.ПолучитьОбъект();
СтрокаСостава = ПрайсОбъект.Состав.Найти(ИскомаяНоменклатура, "ВидЛома");

Если СтрокаСостава <> Неопределено Тогда
    Цена = СтрокаСостава.Цена;
Иначе
    Сообщить("Цена для данной номенклатуры не найдена в указанном прайсе!");
КонецЕсли;

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

Способ 3. Переход к профессиональному стандарту (Регистры сведений)

Как справедливо отметили участники форума в сообщениях 3 и 6, хранение цен исключительно в документах — это не самая удачная архитектура для 1С. Документ фиксирует факт изменения (событие), а для хранения актуальных срезов информации (цены, остатки, курсы валют) предназначены Регистры сведений.

Давайте выясним, почему регистр лучше:

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


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

Как реализовать вызов при выборе номенклатуры

Чтобы цена подтягивалась автоматически в момент выбора товара в табличной части, нам нужно прописать логику в модуле формы документа «ПСА». Рассмотрим алгоритм действий:

  1. Откройте форму документа и найдите таблицу (табличную часть).
  2. Для реквизита Номенклатура (или ВидЛома) создайте обработчик события ПриИзменении.
  3. В обработчике вызовите серверную процедуру для получения цены.

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


&НаКлиенте
Процедура СоставНоменклатураПриИзменении(Элемент)
    
    ТекущаяСтрока = Элементы.Состав.ТекущиеДанные;
    
    Если ТекущаяСтрока <> Неопределено Тогда
        // Передаем номенклатуру на сервер и получаем цену
        ТекущаяСтрока.Цена = ПолучитьЦенуНаСервере(ТекущаяСтрока.Номенклатура);
        
        // Пересчитываем сумму строки, если нужно
        ТекущаяСтрока.Сумма = ТекущаяСтрока.Количество * ТекущаяСтрока.Цена;
    КонецЕсли;
    
КонецПроцедуры

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

← На главную