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