Как вывести серии и остатки номенклатуры в списке выбора в 1С:Управление торговлей?

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

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

Общий подход к доработке: Использование расширений

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

  1. Простота обновлений: Мы можем безболезненно обновлять типовую конфигурацию, не опасаясь потери наших доработок или возникновения конфликтов.
  2. Снижение рисков: Риск внесения ошибок в основной код программы минимален, так как все изменения изолированы в расширении.
  3. Легкость развертывания: Расширения легко подключаются и отключаются, что удобно для тестирования и переноса между базами.

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

Решение 1: Переопределение процедуры ОбработкаПолученияДанныхВыбора

Одно из наиболее универсальных и мощных мест для изменения стандартного поведения выпадающих списков выбора в 1С — это процедура ОбработкаПолученияДанныхВыбора. Давайте рассмотрим ее подробнее.

Что это за процедура и где она находится?

Процедура ОбработкаПолученияДанныхВыбора располагается в модуле менеджера справочника, который мы хотим изменить. В нашем случае это будет МодульМенеджераСправочника.Номенклатура. Эта процедура вызывается платформой 1С в различных сценариях:

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

Параметры процедуры ОбработкаПолученияДанныхВыбора

Давайте посмотрим на основные параметры, которые эта процедура принимает:

Реализация шаги за шагом

Мы будем действовать следующим образом:

  1. Создаем или открываем расширение: В конфигураторе создаем новое расширение или открываем существующее.

  2. Добавляем справочник Номенклатура в расширение: Перетаскиваем справочник Номенклатура в расширение, а затем, в свойствах справочника, устанавливаем для поля "Модуль менеджера" свойство "Заимствовать с изменением". Это позволит нам добавить свой код в модуль менеджера.

  3. Переопределяем процедуру: В модуле менеджера справочника Номенклатура мы добавляем или модифицируем процедуру ОбработкаПолученияДанныхВыбора.

  4. Получаем строку поиска: Из параметра Параметры мы извлекаем текст, который пользователь ввел в поле.

  5. Формируем запрос к базе данных: Нам потребуется запрос, который объединит информацию из справочника Номенклатура, регистра накопления ОстаткиТоваровНаСкладах (для получения информации об остатках) и, если учет серий ведется, регистра сведений СерииНоменклатуры (или другого регистра, где хранятся серии). Мы будем фильтровать результаты по введенной строке поиска.

    Примерная структура запроса может выглядеть так:

    
    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |    Номенклатура.Ссылка,
    |    Номенклатура.Наименование,
    |    Номенклатура.Артикул,
    |    ЕСТЬNULL(Серии.Серия, ""<Без серии>"") КАК Серия,
    |    ЕСТЬNULL(Остатки.ВНаличииОстаток, 0) КАК ОстатокНаСкладе
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура
    |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваровНаСкладах.Остатки КАК Остатки
    |        ПО Номенклатура.Ссылка = Остатки.Номенклатура
    |            И Остатки.Склад = &ОсновнойСклад
    |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СерииНоменклатуры КАК Серии
    |        ПО Номенклатура.Ссылка = Серии.Номенклатура
    |ГДЕ
    |    Номенклатура.Наименование ПОДОБНО &СтрокаПоиска
    |    И Номенклатура.ПометкаУдаления = ЛОЖЬ
    |
    |УПОРЯДОЧИТЬ ПО
    |    Номенклатура.Наименование";
    
    Запрос.УстановитьПараметр("ОсновнойСклад", Справочники.Склады.НайтиПоНаименованию("Основной склад")); // Или другим способом получить ссылку на основной склад
    Запрос.УстановитьПараметр("СтрокаПоиска", "%" + СтрокаПоиска + "%");
    
    РезультатЗапроса = Запрос.Выполнить().Выгрузить();
    
  6. Заполняем параметр ДанныеВыбора: Мы итерируем по результатам запроса и для каждой строки формируем элемент для списка ДанныеВыбора. Каждый элемент будет представлен структурой, где Значение — это ссылка на номенклатуру, а Представление — это строка, которую увидит пользователь (например, "Наименование [Серия] (Остаток: X шт.)").

    
    Для Каждого СтрокаРезультата Из РезультатЗапроса Цикл
        НовыйЭлемент = Новый Структура;
        НовыйЭлемент.Вставить("Значение", СтрокаРезультата.Ссылка);
        НовыйЭлемент.Вставить("Представление",
            СтрокаРезультата.Наименование + " [" + СтрокаРезультата.Серия + "] " +
            "(Остаток: " + Формат(СтрокаРезультата.ОстатокНаСкладе, "ЧЦ=15; ЧДЦ=0") + " шт.)");
        ДанныеВыбора.Добавить(НовыйЭлемент);
    КонецЦикла;
    
  7. Отключаем стандартную обработку: Чтобы платформа не добавляла свои стандартные варианты выбора, мы должны установить параметр СтандартнаяОбработка в Ложь.

    СтандартнаяОбработка = Ложь;

Таким образом, наша процедура ОбработкаПолученияДанныхВыбора будет выглядеть примерно так:


Процедура ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Параметры, СтандартнаяОбработка, ОбъектМетаданных) Экспорт

    // Получаем строку поиска, введенную пользователем
    СтрокаПоиска = Параметры.СтрокаПоиска;
    Если ПустаяСтрока(СтрокаПоиска) Тогда
        Возврат; // Если строка поиска пуста, не показываем ничего или стандартный список
    КонецЕсли;

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

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

    Запрос.УстановитьПараметр("ОсновнойСклад", ОсновнойСклад);
    Запрос.УстановитьПараметр("СтрокаПоиска", "%" + СтрокаПоиска + "%");

    РезультатЗапроса = Запрос.Выполнить().Выгрузить();

    // Отключаем стандартную обработку, чтобы полностью контролировать список выбора
    СтандартнаяОбработка = Ложь;

    // Заполняем список выбора
    Для Каждого СтрокаРезультата Из РезультатЗапроса Цикл
        НовыйЭлемент = Новый Структура;
        НовыйЭлемент.Вставить("Значение", СтрокаРезультата.Ссылка);
        НовыйЭлемент.Вставить("Представление",
            СтрокаРезультата.Наименование + " [" + СтрокаРезультата.Серия + "] " +
            "(Остаток: " + Формат(СтрокаРезультата.ОстатокНаСкладе, "ЧЦ=15; ЧДЦ=0") + " шт.)");
        ДанныеВыбора.Добавить(НовыйЭлемент);
    КонецЦикла;

КонецПроцедуры

Решение 2: Обработчик события АвтоПодбор для поля формы

Иногда нам не требуется изменять поведение выбора номенклатуры во всей системе, а лишь в конкретных документах или формах (например, только в "Заказе клиента"). В таких случаях, вместо глобального переопределения процедуры в модуле менеджера справочника, мы можем использовать обработчик события АвтоПодбор для конкретного поля ввода номенклатуры в модуле формы документа.

Когда применять этот подход?

Этот вариант подходит, если:

Как это работает?

  1. Открываем форму документа: В конфигураторе открываем форму нужного документа (например, Документ.ЗаказКлиента.ФормаДокумента).

  2. Находим поле ввода номенклатуры: В элементах формы находим поле, отвечающее за ввод номенклатуры (например, ПолеНоменклатура).

  3. Создаем обработчик события АвтоПодбор: В свойствах этого поля переходим на закладку "События" и находим событие АвтоПодбор. Создаем для него обработчик.

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

    Примерная структура обработчика:

    
    Процедура ТоварыНоменклатураАвтоПодбор(Элемент, Текст, ЗначениеВыбора, СтандартнаяОбработка)
    
        Если ПустаяСтрока(Текст) Тогда
            Возврат;
        КонецЕсли;
    
        // ... (Аналогичный запрос к базе данных, как в примере выше) ...
        // Запрос должен учитывать, что в контексте формы может быть доступен,
        // например, основной склад из настроек документа или пользователя.
    
        Запрос = Новый Запрос;
        Запрос.Текст =
        "ВЫБРАТЬ
        |    Номенклатура.Ссылка,
        |    Номенклатура.Наименование,
        |    ВЫБОР
        |        КОГДА СерииНоменклатуры.Серия ЕСТЬ NULL
        |            ТОГДА ""<Без серии>""
        |        ИНАЧЕ СерииНоменклатуры.Серия
        |    КОНЕЦ КАК Серия,
        |    ЕСТЬNULL(ОстаткиТоваровНаСкладахОстатки.ВНаличииОстаток, 0) КАК ОстатокНаСкладе
        |ИЗ
        |    Справочник.Номенклатура КАК Номенклатура
        |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваровНаСкладах.Остатки(
        |                ,
        |                Склад = &ОсновнойСклад
        |                И Номенклатура В
        |                    (ВЫБРАТЬ
        |                        Номенклатура.Ссылка
        |                    ИЗ
        |                        Справочник.Номенклатура КАК Номенклатура
        |                    ГДЕ
        |                        Номенклатура.Наименование ПОДОБНО &СтрокаПоиска
        |                        И Номенклатура.ПометкаУдаления = ЛОЖЬ)) КАК ОстаткиТоваровНаСкладахОстатки
        |        ПО Номенклатура.Ссылка = ОстаткиТоваровНаСкладахОстатки.Номенклатура
        |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СерииНоменклатуры КАК СерииНоменклатуры
        |        ПО Номенклатура.Ссылка = СерииНоменклатуры.Номенклатура
        |ГДЕ
        |    Номенклатура.Наименование ПОДОБНО &СтрокаПоиска
        |    И Номенклатура.ПометкаУдаления = ЛОЖЬ
        |
        |УПОРЯДОЧИТЬ ПО
        |    Номенклатура.Наименование,
        |    Серия";
    
        ОсновнойСклад = Справочники.Склады.НайтиПоНаименованию("Основной склад"); // Или получить из настроек документа/пользователя
        Если ОсновнойСклад = Неопределено Тогда
            Возврат;
        КонецЕсли;
    
        Запрос.УстановитьПараметр("ОсновнойСклад", ОсновнойСклад);
        Запрос.УстановитьПараметр("СтрокаПоиска", "%" + Текст + "%");
    
        РезультатЗапроса = Запрос.Выполнить().Выгрузить();
    
        СтандартнаяОбработка = Ложь; // Отключаем стандартную обработку
    
        Для Каждого СтрокаРезультата Из РезультатЗапроса Цикл
            НовыйЭлемент = Новый Структура;
            НовыйЭлемент.Вставить("Значение", СтрокаРезультата.Ссылка);
            НовыйЭлемент.Вставить("Представление",
                СтрокаРезультата.Наименование + " [" + СтрокаРезультата.Серия + "] " +
                "(Остаток: " + Формат(СтрокаРезультата.ОстатокНаСкладе, "ЧЦ=15; ЧДЦ=0") + " шт.)");
            ЗначениеВыбора.Добавить(НовыйЭлемент);
        КонецЦикла;
    
    КонецПроцедуры
    

Этот подход более точечный и позволяет нам гибко настраивать отображение в разных местах.

Важные аспекты учета серий и остатков в 1С:УТ

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

  1. Учет серий номенклатуры:

    • В 1С:УТ учет серий настраивается на уровне вида номенклатуры. Мы должны перейти в раздел НСИ и администрирование -> Настройки НСИ и разделов -> Номенклатура -> Настройки создания и там найти Виды номенклатуры.
    • Для вида номенклатуры, к которому относится наш товар (например, "Лекарства"), необходимо настроить политику учета серий. Важно, чтобы был выбран вариант "Учет остатков по сериям" или "Учет себестоимости товаров по сериям". Только в этом случае информация о сериях будет фиксироваться в остатках и будет доступна для запроса.
    • Если выбрана опция "Справочное указание серий", то серии будут фиксироваться в документах, но не будут контролироваться в остатках, и наш запрос не сможет получить их из регистра остатков.
    • Мы также должны убедиться, что для нашей номенклатуры в карточке товара указан правильный вид номенклатуры.
  2. Учет остатков товаров на складах:

    • Убедимся, что в разделе НСИ и администрирование -> Настройки НСИ и разделов -> Склад и доставка установлен флажок "Учет товаров на складах".
    • Если используется адресное хранение или другие специфические настройки склада, это может повлиять на запрос остатков. Нам нужно учитывать эти особенности при формировании запроса.
    • Важно также, чтобы документы поступления и реализации корректно проводились, формируя движения по регистру ОстаткиТоваровНаСкладах.

Без правильно настроенного учета в 1С наш код, даже самый совершенный, не сможет получить нужные данные.

Производительность: потенциальные "тормоза" и оптимизация

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

Причины замедления

Меры по оптимизации

  1. Оптимизация запросов:

    • Выбирайте только необходимое: В запросе указывайте только те поля, которые вам нужны. Избегайте ВЫБРАТЬ *.
    • Используйте индексы: Убедитесь, что поля, по которым происходит фильтрация (например, Номенклатура.Наименование) и соединение таблиц (например, Номенклатура.Ссылка, ОстаткиТоваровНаСкладах.Номенклатура, СерииНоменклатуры.Номенклатура), проиндексированы в базе данных. Для типовых регистров и справочников, как правило, индексы уже есть, но для пользовательских или специфических настроек это стоит проверить.
    • Ограничивайте количество записей: В некоторых случаях можно ограничить количество строк, возвращаемых запросом (например, ПЕРВЫЕ 50), чтобы не загружать слишком много данных, если их не нужно показывать.
    • Уточняйте условия: Чем точнее фильтры в запросе (ГДЕ), тем меньше данных придется обрабатывать.
  2. Кэширование данных:

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

  3. Тщательное тестирование:

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

Альтернатива: "Подбор номенклатуры через корзину"

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

Что это такое?

"Подбор через корзину" — это, по сути, отдельная, специально разработанная форма, которая открывается по нажатию кнопки (например, "Подбор") в табличной части документа. Эта форма предоставляет гораздо более широкий функционал, чем простой выпадающий список:

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

Отличие от полнотекстового поиска

Важно провести четкое различие между механизмом, который мы обсуждаем, и полнотекстовым поиском (ПТП).

Мы сконцентрированы именно на стандартном механизме выбора, так как он более приспособлен для оперативной работы в полях ввода.

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

← На главную