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