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