Как реализовать подбор номенклатуры в 1С с автозаполнением статьи финансирования и места хранения из регистра

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

При разработке складского или товарного учета в среде 1С разработчики часто сталкиваются с фундаментальной архитектурной задачей: как правильно связать статичные свойства номенклатуры и ее динамические учетные характеристики. К статичным свойствам относятся наименование, артикул, базовая единица измерения или производитель. Динамические характеристики — это то, чем товар «обрастает» в процессе его движения: конкретная партия поставки, источник или статья финансирования, а также физическое место хранения на складе.

Попытка хранить динамические данные непосредственно в справочнике Номенклатура является грубой архитектурной ошибкой. В реальной практике одна и та же номенклатурная позиция может прийти по разным статьям финансирования и быть разложена по разным ячейкам (местам хранения) склада. Единственно верное место для хранения таких данных — это регистры (накопления или сведений). Но как организовать удобный подбор для пользователя, чтобы при выборе товара в документ автоматически подтягивались его текущее место хранения и статья финансирования? Разберем эту задачу по шагам на платформе «1С:Предприятие 8.3» с использованием управляемых форм.

Архитектурная концепция: Связь справочника и регистра

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

Для реализации удобного подбора нам необходимо объединить эти данные на уровне интерфейса. Инструментом для такого объединения выступает Динамический список с произвольным запросом. Мы выбираем данные из справочника номенклатуры и левым соединением подключаем к нему виртуальную таблицу остатков нашего регистра.

Рассмотрим структуру запроса для динамического списка формы выбора или подбора:


ВЫБРАТЬ
    СправочникНоменклатура.Ссылка КАК Ссылка,
    СправочникНоменклатура.Наименование КАК Наименование,
    СправочникНоменклатура.Артикул КАК Артикул,
    ЕСТЬNULL(ОстаткиТоваров.МестоХранения, ЗНАЧЕНИЕ(Справочник.МестаХранения.ПустаяСсылка)) КАК МестоХранения,
    ЕСТЬNULL(ОстаткиТоваров.СтатьяФинансирования, ЗНАЧЕНИЕ(Справочник.СтатьиФинансирования.ПустаяСсылка)) КАК СтатьяФинансирования,
    ЕСТЬNULL(ОстаткиТоваров.ВНаличииОстаток, 0) КАК Остаток
ИЗ
    Справочник.Номенклатура КАК СправочникНоменклатура
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваров.Остатки(&Период, Склад = &ПараметрСклад) КАК ОстаткиТоваров
    ПО ОстаткиТоваров.Номенклатура = СправочникНоменклатура.Ссылка

Обратим внимание на важный нюанс: использование функции ЕСТЬNULL. Если товара нет на складе, левое соединение вернет значение Null. Функция ЕСТЬNULL заменяет пустые значения на пустые ссылки соответствующих справочников или нулевой остаток, что предотвращает появление пустых полей и системных ошибок в интерфейсе пользователя.

Метод 1. Одиночный выбор с передачей структуры данных (через ОповеститьОВыборе)

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

Для реализации этой логики мы переопределим стандартное поведение платформы и передадим вместо одиночной ссылки объект типа Структура.

Шаг 1. Перехват выбора в форме выбора номенклатуры

Откроем форму выбора справочника Номенклатура. Для таблицы динамического списка (назовем ее Список) создадим обработчик события Выбор на клиенте и пропишем в нем следующий алгоритм:


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

Шаг 2. Инициация выбора из формы документа

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


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

Шаг 3. Обработка полученных данных в документе

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


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

Метод ЗаполнитьЗначенияСвойств сопоставляет ключи структуры (Номенклатура, МестоХранения, СтатьяФинансирования) с именами колонок табличной части документа и мгновенно заполняет всю строку. Это избавляет нас от ручного написания присвоений для каждого реквизита.

Метод 2. Множественный подбор номенклатуры (Режим «Корзины»)

Если пользователю необходимо за одну сессию выбрать сразу несколько десятков позиций, постоянное открытие и закрытие одиночной формы выбора будет неэффективным. Для этого проектируется полноценный механизм подбора с так называемой «Корзиной» (таблицей на форме подбора, куда накапливаются выбранные товары до момента их окончательного переноса в документ).

Рассмотрим, как правильно реализовать передачу целой таблицы данных. Платформа 1С на клиентском уровне накладывает ограничение: мы не можем напрямую передать объект типа ТаблицаЗначений между формами через ОповеститьОВыборе из-за особенностей сериализации данных. Выделим два профессиональных подхода к обходу этого ограничения.

Вариант А. Передача через массив структур (для небольших и средних документов)

Этот способ прост в реализации и отлично подходит для документов, где количество подбираемых строк за один раз не превышает нескольких сотен. В форме подбора при нажатии на кнопку «Перенести в документ» мы обходим коллекцию строк корзины, упаковываем каждую строку в структуру, собираем их в массив и отправляем клиенту-владельцу:


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

В форме документа в обработчике ОбработкаВыбора мы принимаем этот массив и загружаем данные:


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

Вариант Б. Передача через Временное Хранилище (Enterprise-стандарт)

Если объемы переносимых данных велики или требуется максимальная производительность системы, используется механизм временного хранилища. Это стандарт разработки, принятый в типовых конфигурациях фирмы «1С» (ERP, Управление торговлей, Комплексная автоматизация).

Алгоритм действий следующий:

  1. В форме подбора таблица корзины на сервере выгружается в реальную ТаблицаЗначений.
  2. Эта таблица помещается во временное хранилище, связанное с уникальным идентификатором формы.
  3. В метод ОповеститьОВыборе передается только строковый адрес во временном хранилище.
  4. Форма документа принимает этот адрес, переходит на сервер, извлекает таблицу и быстро загружает ее в объект.

Посмотрим на программную реализацию в форме подбора:


&НаКлиенте
Процедура ПеренестиВДокумент(Команда)
    
    // Помещаем таблицу в хранилище на сервере и получаем адрес
    АдресХранилища = ПоместитьТаблицуВХранилищеНаСервере();
    
    // Передаем только адрес (строку) в форму документа
    ОповеститьОВыборе(АдресХранилища);
    
    Закрыть();
    
КонецПроцедуры

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

Прием данных в форме документа выглядит лаконично и работает чрезвычайно быстро благодаря использованию метода Загрузить:


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

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

Параметризация динамического списка по Складу

Чтобы в форме подбора выводились корректные остатки, места хранения и статьи финансирования, динамический список должен знать контекст нашего документа — конкретный Склад, выбранный в шапке заявки. Если мы не передадим склад, то запрос левого соединения выдаст некорректные остатки (смешает остатки со всех складов компании).

Для этого настроим параметризацию динамического списка формы выбора при ее создании:


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

Такой подход гарантирует, что менеджер или кладовщик при оформлении заявки всегда увидит реальное распределение остатков товаров по местам хранения и статьям финансирования именно в рамках выбранного склада, исключая ошибки человеческого фактора.

Резюме и рекомендации

Мы детально разобрали архитектурные основы проектирования подбора в системе «1С:Предприятие 8». Использование связки динамического списка с виртуальными таблицами остатков регистров позволяет оперативно выводить пользователю комплексную аналитическую картину. Передача данных посредством структур, массивов структур или через временное хранилище обеспечивает высокую скорость работы приложения, сохраняя чистоту кода и гибкость его поддержки в будущем.

← На главную