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