В процессе разработки в системе «1С:Предприятие» часто возникает задача подтянуть данные из одного документа в другой. Типичный пример: при выборе номенклатуры в документе «Приемо-сдаточный акт» (ПСА) необходимо автоматически подставить цену, которая указана в актуальном документе «Прайс-лист». В данной статье мы подробно разберем, как правильно обратиться к табличной части стороннего документа, проанализируем ошибки начинающих разработчиков и рассмотрим наиболее эффективные способы решения этой задачи.
Обращение к данным через запрос — это наиболее производительный и правильный способ с точки зрения платформенных механизмов. Давайте разберем, почему вариант прямого перебора всех строк табличной части (как в сообщении 5) является ошибочным и как его исправить.
Основная проблема кода из примера заключалась в том, что запрос выбирал все цены из всех существующих документов «Прайс-лист», а цикл возвращал цену первой попавшейся строки. Чтобы получить корректные данные, мы должны использовать параметры и условия.
Рассмотрим правильную реализацию функции получения цены на сервере:
&НаСервере
Функция ПолучитьЦенуНоменклатуры(АктуальнаяНоменклатура, СсылкаНаПрайс) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПрайсЛистСостав.Цена КАК Цена
|ИЗ
| Документ.ПрайсЛист.Состав КАК ПрайсЛистСостав
|ГДЕ
| ПрайсЛистСостав.Ссылка = &СсылкаНаПрайс
| И ПрайсЛистСостав.ВидЛома = &Номенклатура";
Запрос.УстановитьПараметр("СсылкаНаПрайс", СсылкаНаПрайс);
Запрос.УстановитьПараметр("Номенклатура", АктуальнаяНоменклатура);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
Возврат Выборка.Цена;
КонецЕсли;
Возврат 0;
КонецФункции
Важные замечания по коду:
ГДЕ позволяет базе данных моментально найти нужную строку, не сканируя лишние документы.&СсылкаНаПрайс критически важен, так как в системе может быть множество прайс-листов. Вы должны четко указать, из какого именно документа брать цену (например, из реквизита объекта или выбрав последний по дате).Пустой(), чтобы избежать ошибок, если номенклатура не найдена в прайсе.Иногда, если у нас уже есть ссылка на документ в памяти, можно воспользоваться методом Найти() самой табличной части. Рассмотрим, как это работает на практике.
Предположим, у нас есть переменная ДокументОбъект, содержащая ссылку на нужный прайс-лист. Мы можем осуществить поиск следующим образом:
// Допустим, ПрайсСсылка — это ссылка на нужный документ ПрайсЛист
ПрайсОбъект = ПрайсСсылка.ПолучитьОбъект();
СтрокаСостава = ПрайсОбъект.Состав.Найти(ИскомаяНоменклатура, "ВидЛома");
Если СтрокаСостава <> Неопределено Тогда
Цена = СтрокаСостава.Цена;
Иначе
Сообщить("Цена для данной номенклатуры не найдена в указанном прайсе!");
КонецЕсли;
Проанализируем ситуацию: данный метод кажется проще, но у него есть существенный недостаток. При вызове ПолучитьОбъект() система считывает из базы данных весь документ целиком, включая все его реквизиты и все строки табличных частей. Если в прайс-листе тысячи позиций, это приведет к избыточной нагрузке на сеть и сервер. Поэтому данный метод стоит использовать только в тех случаях, когда документ уже и так загружен в память или количество строк в нем минимально.
Как справедливо отметили участники форума в сообщениях 3 и 6, хранение цен исключительно в документах — это не самая удачная архитектура для 1С. Документ фиксирует факт изменения (событие), а для хранения актуальных срезов информации (цены, остатки, курсы валют) предназначены Регистры сведений.
Давайте выясним, почему регистр лучше:
Если бы цены хранились в периодическом регистре сведений ЦеныНоменклатуры, получение цены выглядело бы так:
&НаСервере
Функция ПолучитьАктуальнуюЦену(Номенклатура, ДатаПСА)
Менеджер = РегистрыСведений.ЦеныНоменклатуры;
ПараметрыОтбора = Новый Структура("Номенклатура", Номенклатура);
// Получаем цену на дату документа
Ресурс = Менеджер.СрезПоследних(ДатаПСА, ПараметрыОтбора);
Если Ресурс.Количество() > 0 Тогда
Возврат Ресурс[0].Цена;
КонецЕсли;
Возврат 0;
КонецФункции
Чтобы цена подтягивалась автоматически в момент выбора товара в табличной части, нам нужно прописать логику в модуле формы документа «ПСА». Рассмотрим алгоритм действий:
Номенклатура (или ВидЛома) создайте обработчик события ПриИзменении.Пример кода на стороне клиента:
&НаКлиенте
Процедура СоставНоменклатураПриИзменении(Элемент)
ТекущаяСтрока = Элементы.Состав.ТекущиеДанные;
Если ТекущаяСтрока <> Неопределено Тогда
// Передаем номенклатуру на сервер и получаем цену
ТекущаяСтрока.Цена = ПолучитьЦенуНаСервере(ТекущаяСтрока.Номенклатура);
// Пересчитываем сумму строки, если нужно
ТекущаяСтрока.Сумма = ТекущаяСтрока.Количество * ТекущаяСтрока.Цена;
КонецЕсли;
КонецПроцедуры
Подведем итоги: для разовых задач или специфических условий можно использовать запрос к табличной части документа с обязательным указанием ссылки на конкретный документ и конкретную номенклатуру. Однако, если ваша система предполагает частое обновление и использование цен, правильным решением будет внедрение регистра сведений. Это обеспечит стабильность и высокую скорость работы программы при росте объема данных.