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

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

При разработке пользовательских интерфейсов в 1С часто возникает задача реализации удобного механизма подбора. Например, когда пользователю нужно открыть вспомогательное окно, выбрать там необходимые позиции номенклатуры (возможно, сразу указав количество и цену) и перенести их в основную форму документа или обработки. Ситуация усложняется, если данные для подбора формируются динамически (например, сложным запросом) и их нужно корректно передать между формами.

В этой статье мы подробно рассмотрим, как правильно спроектировать и закодировать механизм подбора на управляемых формах в платформе 1С:Предприятие 8.3. Мы разберем передачу данных через временное хранилище, обработку выбора на стороне основной формы, исключение дублирования строк, а также рассмотрим профессиональный паттерн проектирования подбора с использованием "Корзины", аналогичный механизмам в типовых конфигурациях (например, "Управление торговлей 11").

Шаг 1. Архитектура взаимодействия управляемых форм при подборе

На платформе 1С v8.3 управляемые формы работают в разделенном контексте "Клиент-Сервер". Прямая передача больших таблиц значений между формами "в лоб" через клиентские параметры неэффективна, так как вызывает избыточную сериализацию данных и нагружает сеть. Правильный архитектурный шаблон взаимодействия выглядит следующим образом:

  1. Основная форма: Выполняет запрос на сервере, получает таблицу значений (ТЗ) с остатками и ценами.
  2. Временное хранилище: Основная форма помещает ТЗ во временное хранилище и получает уникальный адрес (строку).
  3. Открытие формы подбора: Основная форма открывает форму подбора, передавая адрес временного хранилища в параметрах.
  4. Форма подбора: При создании на сервере извлекает ТЗ по адресу и загружает в свой реквизит формы типа ТаблицаЗначений.
  5. Интерактивный выбор: Пользователь отмечает нужные строки.
  6. Оповещение о выборе: Форма подбора отправляет выбранные данные обратно через стандартный механизм платформы.
  7. Обработка выбора: Основная форма принимает массив данных и добавляет его в свою табличную часть, анализируя дубли.

Давайте пошагово реализуем этот алгоритм в кодовой базе нашей обработки.

Шаг 2. Подготовка и передача данных из основной формы

Предположим, на основной форме обработки у нас есть кнопка "Подбор". Напишем обработчик нажатия этой кнопки. Нам потребуется сформировать таблицу значений запросом на сервере, поместить её во временное хранилище и открыть форму подбора.

Обратите внимание на использование свойства формы УникальныйИдентификатор. Передавая его в функцию помещения в хранилище, мы привязываем время жизни этих данных к жизненному циклу самой основной формы.


&НаКлиенте
Процедура КомандаПодбор(Команда)
    
    // Получаем адрес временного хранилища с заполненной ТЗ
    АдресДанныхПодбора = ПолучитьДанныеДляПодбораНаСервере();
    
    // Формируем параметры для открытия формы подбора
    ПараметрыПодбора = Новый Структура;
    ПараметрыПодбора.Вставить("АдресДанных", АдресДанныхПодбора);
    
    // Открываем форму подбора нашей обработки
    // Третьим параметром передаем ЭтаФорма, чтобы сработал механизм ОбработкаВыбора
    ОткрытьФорму("Обработка.МояОбработка.Форма.ФормаПодбора", ПараметрыПодбора, ЭтаФорма);
    
КонецПроцедуры

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

Шаг 3. Прием данных и отображение их в форме подбора

Теперь перейдем к форме подбора (создайте в обработке вторую форму с именем ФормаПодбора). На этой форме нам нужно отобразить полученные данные. Для этого создадим реквизит формы с именем ТаблицаПодбора и типом ТаблицаЗначений. Добавим в эту таблицу колонки: Номенклатура (СправочникСсылка.Номенклатура), Серия (СправочникСсылка.СерииНоменклатуры), Количество (Число), Цена (Число). Вынесем этот реквизит на форму в виде элемента "Таблица".

Чтобы разрешить пользователю выбирать сразу несколько строк с помощью клавиш Ctrl или Shift, обязательно настроим свойство элемента формы таблицы: установим Режим выделения в значение Множественный.

В модуле формы подбора пропишем обработчик события ПриСозданииНаСервере, где извлечем переданные данные:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Проверяем наличие параметра с адресом временного хранилища
    Если Параметры.Свойство("АдресДанных") Тогда
        
        // Получаем таблицу значений из хранилища
        ТаблицаИзХранилища = ПолучитьИзВременногоХранилища(Параметры.АдресДанных);
        
        Если ТипЗнч(ТаблицаИзХранилища) = Тип("ТаблицаЗначений") Тогда
            // Заполняем реквизит управляемой формы данными таблицы значений
            ЗначениеВРеквизитФормы(ТаблицаИзХранилища, "ТаблицаПодбора");
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Шаг 4. Отправка выбранных строк обратно в основную форму

Когда пользователь отметил нужные строки, он должен нажать кнопку выбора (например, "Перенести в документ"). Напишем обработчик для этой кнопки на клиенте. Мы соберем все выделенные строки в массив структур и передадим его в вызывающую форму с помощью метода ОповеститьОВыборе().


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

Шаг 5. Прием данных и обработка дублей в основной форме

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

Для эффективного поиска дубликатов на сервере мы применим метод НайтиСтроки() табличной части 1С.


&НаКлиенте
Процедура ОбработкаВыбора(РезультатВыбора, ИсточникВыбора)
    
    // Проверяем, что к нам пришел массив строк
    Если ТипЗнч(РезультатВыбора) = Тип("Массив") Тогда
        
        // Передаем обработку на сервер для работы с табличной частью
        ДобавитьПодобранныеТоварыНаСервере(РезультатВыбора);
        
    КонецЕсли;
    
КонецПроцедуры

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

Шаг 6. Продвинутый вариант подбора с "Корзиной" (как в ERP и УТ 11)

Рассмотрим более эргономичный и современный вариант, упомянутый во втором сообщении форума. В типовых конфигурациях пользователь не просто выделяет строки в общем списке, а переносит их двойным кликом в нижнюю таблицу формы подбора — "Корзину". В корзине пользователь может сразу скорректировать количество и цену, а затем перенести накопленный результат в документ одной кнопкой. Это исключает необходимость многократно открывать и закрывать форму подбора.

Для реализации этого механизма на форме подбора добавим второй реквизит типа ТаблицаЗначений с именем Корзина (с аналогичной структурой колонок) и выведем его на форму ниже основной таблицы.

Настроим событие Выбор (двойной клик) для верхней таблицы подбора:


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

&НаКлиенте
Процедура ДобавитьТоварВКорзину(ДанныеТовара)
    
    // Ищем товар в корзине на клиенте, чтобы избежать лишнего серверного вызова
    ПараметрыПоиска = Новый Структура("Номенклатура, Серия", ДанныеТовара.Номенклатура, ДанныеТовара.Серия);
    НайденныеСтрокиКорзины = Корзина.НайтиСтроки(ПараметрыПоиска);
    
    Если НайденныеСтрокиКорзины.Количество() > 0 Тогда
        // Товар уже в корзине, увеличиваем количество на 1
        СтрокаКорзины = НайденныеСтрокиКорзины[0];
        СтрокаКорзины.Количество = СтрокаКорзины.Количество + 1;
    Иначе
        // Добавляем новую строку в корзину
        НоваяСтрока = Корзина.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, ДанныеТовара);
        НоваяСтрока.Количество = 1; // По умолчанию добавляем 1 штуку
    КонецЕсли;
    
КонецПроцедуры

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


&НаКлиенте
Процедура ПеренестиКорзинуВДокумент(Команда)
    
    МассивРезультата = Новый Массив;
    
    Для Каждого СтрокаКорзины Из Корзина Цикл
        
        СтруктураСтроки = Новый Структура("Номенклатура, Серия, Количество, Цена");
        ЗаполнитьЗначенияСвойств(СтруктураСтроки, СтрокаКорзины);
        МассивРезультата.Добавить(СтруктураСтроки);
        
    КонецЦикла;
    
    ОповеститьОВыборе(МассивРезультата);
    
КонецПроцедуры

Заключение и важные рекомендации

Мы успешно реализовали гибкий и производительный механизм подбора в управляемом приложении 1С 8.3. Давайте резюмируем ключевые правила разработки таких интерфейсов:

← На главную