В типовой конфигурации 1С:Управление торговлей редакции 10.3 механизм подбора товаров является одним из самых часто используемых инструментов. Однако стандартная логика не всегда удовлетворяет требованиям бизнеса. Например, может возникнуть задача: при подборе товара в документ реализации или заказ покупателя необходимо видеть и переносить не просто остаток на одном основном складе, а свободный остаток (за вычетом резервов) по определенному списку складов. Рассмотрим, как программно реализовать эту задачу, проанализируем структуру запросов и разберем способы оптимизации кода.
Прежде чем приступать к написанию кода, разберем, как работает подбор в УТ 10.3. Подбор обычно вызывается из формы документа и представляет собой отдельную обработку ПодборНоменклатуры. Когда пользователь выбирает товар, обработка возвращает данные в документ. Основная сложность заключается в том, что расчет остатков может происходить как на стороне обработки подбора (для визуализации), так и на стороне документа (в момент окончательного расчета перед добавлением в табличную часть).
Проанализируем ситуацию, когда нам нужно программно вычесть определенное значение (например, зарезервированные товары) из общего остатка по пяти конкретным складам. Для этого нам потребуется модифицировать запрос к регистрам накопления.
Рассмотрим вариант реализации, предложенный в обсуждении. Суть метода заключается в создании массива складов и выполнении запроса, который объединяет данные из двух регистров: ТоварыНаСкладах (физическое наличие) и ТоварыВРезервеНаСкладах (забронированные товары).
Для начала сформируем список складов. Обратите внимание, что поиск по коду является жестко заданным условием (хардкодом), поэтому в реальной практике рекомендуется использовать настройки пользователя или группы складов. Однако для понимания логики разберем пример с кодами:
КодС = "00003";
Склад = Справочники.Склады.НайтиПоКоду(КодС);
КодД = "00001";
Склад1 = Справочники.Склады.НайтиПоКоду(КодД);
КодА = "00004";
Склад2 = Справочники.Склады.НайтиПоКоду(КодА);
КодТ = "00005";
Склад4 = Справочники.Склады.НайтиПоКоду(КодТ);
// Создаем массив для передачи в параметры запроса
СписокСкладов = Новый Массив;
СписокСкладов.Добавить(Склад);
СписокСкладов.Добавить(Склад1);
СписокСкладов.Добавить(Склад2);
СписокСкладов.Добавить(Склад4);
Теперь проанализируем структуру запроса, который позволит получить корректный остаток. Мы используем временные таблицы для повышения производительности и читаемости кода. Выясним причину использования ОБЪЕДИНИТЬ ВСЕ: это необходимо, чтобы собрать данные из разных регистров в одну таблицу перед итоговой группировкой.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
| ТоварыНаСкладахОстатки.КоличествоОстаток КАК КоличествоОстаток
|ПОМЕСТИТЬ ВремТЗ1
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура В (&СписокНоменклатуры) И Склад В (&СписокСкладов)) КАК ТоварыНаСкладахОстатки
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ РАЗРЕШЕННЫЕ
| ТоварыВРезервеНаСкладахОстатки.Номенклатура КАК Номенклатура,
| ТоварыВРезервеНаСкладахОстатки.КоличествоОстаток КАК КоличествоОстаток
|ПОМЕСТИТЬ ВремТЗ2
|ИЗ
| РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(, Номенклатура В (&СписокНоменклатуры) И Склад В (&СписокСкладов)) КАК ТоварыВРезервеНаСкладахОстатки
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВремТЗ1.Номенклатура,
| ВремТЗ1.КоличествоОстаток
|ПОМЕСТИТЬ ВремТЗ3
|ИЗ
| ВремТЗ1 КАК ВремТЗ1
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| ВремТЗ2.Номенклатура,
| -ВремТЗ2.КоличествоОстаток
|ИЗ
| ВремТЗ2 КАК ВремТЗ2
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВремТЗ3.Номенклатура,
| СУММА(ВремТЗ3.КоличествоОстаток) КАК КоличествоОстаток
|ИЗ
| ВремТЗ3 КАК ВремТЗ3
|
|СГРУППИРОВАТЬ ПО
| ВремТЗ3.Номенклатура";
Запрос.УстановитьПараметр("СписокНоменклатуры", МассивТоваров_);
Запрос.УстановитьПараметр("СписокСкладов", СписокСкладов);
РезультатЗапроса = Запрос.Выполнить();
В данном запросе мы сначала получаем остатки на складах в ВремТЗ1, затем остатки резервов в ВремТЗ2. В третьем шаге мы объединяем их, при этом резервы берем с отрицательным знаком. Финальный выбор суммирует эти значения по каждой позиции номенклатуры, возвращая реальный свободный остаток.
Рассмотрим более правильный с точки зрения архитектуры 1С подход. Использование поиска по коду делает систему хрупкой. Если в справочнике Склады создать группу (папку) и поместить туда нужные 5 складов, код станет значительно проще и надежнее. Проанализируем изменения в запросе:
Вместо передачи массива складов, мы можем передать одну группу складов. В параметрах виртуальных таблиц условие изменится на Склад В ИЕРАРХИИ (&ГруппаСкладов). Это позволит администратору системы менять состав складов без участия программиста, просто перемещая элементы в справочнике.
Посмотрим на ситуацию с точки зрения использования встроенных возможностей конфигурации. В УТ 10.3 существует общий модуль ОбщегоНазначения, содержащий функции для работы с остатками. Проанализируем функцию ОстаткиИРезервыНоменклатуры. Рассмотрим пример её вызова:
СтруктураПараметров = Новый Структура;
СтруктураПараметров.Вставить("Номенклатура", СсылкаНаНоменклатуру);
СтруктураПараметров.Вставить("Склад", СписокСкладов);
СтруктураПараметров.Вставить("ДатаОстатков", ТекущаяДата());
ТаблицаОстатков = ОбщегоНазначения.ОстаткиИРезервыНоменклатуры(Ложь, СтруктураПараметров);
Использование типовых функций гарантирует, что вы не забудете учесть такие разрезы учета, как Характеристики номенклатуры или Серии, которые крайне важны в торговом учете. Если ваш запрос не учитывает характеристики, а в базе ведется учет по ним, данные подбора будут некорректными.
Выясним, в какой процедуре лучше всего производить пересчет. В УТ 10.3 в форме документа существует процедура ОбработкаПодбора. Именно в ней происходит получение данных из формы подбора. Рассмотрим алгоритм действий:
ОбработкаПодбора(ЗначениеВыбора) в модуле формы документа.ТипЗнч(ЗначениеВыбора). Если это структура с данными о товаре, мы перехватываем поле Количество.Товары.Важный момент: Если вы хотите, чтобы пользователь видел остаток по 5 складам непосредственно в списке выбора (в окне подбора), изменения нужно вносить в саму обработку ПодборНоменклатуры, в процедуру динамического обновления списка. Если же расчет должен происходить только в момент "переноса" в документ, то достаточно правок в форме документа.
Выполнять тяжелые запросы в цикле при подборе каждого товара — плохая практика. Рассмотрим способы ускорения:
ИНДЕКСИРОВАТЬ ПО Номенклатура. Это значительно ускорит процесс сборки данных из разных регистров.Таким образом, мы разобрали, как реализовать сложную логику подбора товаров по нескольким складам, учитывая резервы и специфику конфигурации УТ 10.3. Выбор конкретного метода зависит от того, насколько гибкой должна быть настройка списка складов и требуется ли визуализация этих данных в интерфейсе подбора.