При проведении инвентаризации на крупных складах часто возникает необходимость выполнять проверку не всего склада целиком, а отдельных его зон — стеллажей, полок, ячеек или проходов. В типовых конфигурациях 1С на обычных формах (таких как «Управление торговлей 10.3» или «Управление производственным предприятием 1.3») места хранения номенклатуры носят справочный (информационный) характер. Это означает, что остатки товаров хранятся в разрезе складов, а конкретная полка или стеллаж закрепляется за карточкой товара в регистре сведений МестаХраненияНоменклатуры.
В этой статье мы подробно разберем, как доработать типовой механизм заполнения документа ИнвентаризацияТоваровНаСкладе, чтобы кладовщики могли ежедневно выбирать конкретный стеллаж и заполнять документ остатками только тех товаров, которые к нему привязаны. Мы рассмотрим два надежных способа: прямое изменение кода конфигурации и создание безопасной внешней обработки заполнения табличной части.
Попытка вручную добавить на форму документа поле выбора места хранения и прописать его логику в процедуре «ПриИзменении» — частая ошибка начинающих разработчиков. Дело в том, что заполнение табличной части документа «Инвентаризация товаров на складе» выполняется с помощью специального платформенного объекта — ПостроительОтчета. Этот объект берет на себя всю работу по формированию сложных запросов к остаткам, расчету себестоимости по партиям и динамическому наложению фильтров.
Чтобы отбор по местам хранения заработал корректно, нам нужно внедрить логику фильтрации непосредственно в текст запроса, который использует ПостроительОтчета, а также объявить новое поле в массиве доступных отборов.
---
Этот способ предполагает внесение изменений в код модуля объекта документа. Прежде чем приступить, убедитесь, что ваша база данных снята с поддержки с сохранением возможности изменения.
Нам потребуется отредактировать три ключевые процедуры в модуле объекта документа ИнвентаризацияТоваровНаСкладе:
ЗаполнитьНачальныеНастройкиЗаполнитьПоОстаткамНаСкладеПерезаполнитьУчетныеКоличестваНайдем в модуле объекта процедуру ЗаполнитьНачальныеНастройки(ПостроительОтчета). Она отвечает за инициализацию построителя и формирование списка полей, по которым пользователь сможет делать отбор. Нам необходимо связать таблицу остатков товаров с регистром сведений МестаХраненияНоменклатуры с помощью конструкции необязательного внутреннего соединения.
Проанализируем ситуацию: фигурные скобки {} в языке запросов 1С для построителя отчета указывают на то, что соединение и условия внутри них будут применяться СУБД только тогда, когда пользователь установит соответствующий флажок отбора на форме. Если флажок снят, запрос выполнится в классическом режиме без лишних соединений, что исключает падение производительности базы данных.
Заменим текст запроса в процедуре на следующий вариант:
Процедура ЗаполнитьНачальныеНастройки(ПостроительОтчета) Экспорт
ПостроительОтчета.Текст = "ВЫБРАТЬ
| ОстаткиТоваров.Номенклатура,
| ОстаткиТоваров.ХарактеристикаНоменклатуры," + ?(УчитыватьСерии, "
| ОстаткиТоваров.СерияНоменклатуры,","") + "
| ОстаткиТоваров.Качество,
| СУММА(ОстаткиТоваров.КоличествоОстаток) КАК Количество,
| ВЫБОР
| КОГДА СУММА(ОстаткиПартий.КоличествоОстаток) = 0
| ИЛИ СУММА(ОстаткиПартий.КоличествоОстаток) ЕСТЬ NULL
| ТОГДА 0
| ИНАЧЕ СУММА(ОстаткиПартий.СтоимостьОстаток) * СУММА(ОстаткиТоваров.КоличествоОстаток) / СУММА(ОстаткиПартий.КоличествоОстаток)
| КОНЕЦ КАК Сумма
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(, Склад = &Склад) КАК ОстаткиТоваров
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ПартииТоваровНаСкладах.Остатки(, Склад = &ПартионныйСклад) КАК ОстаткиПартий
| ПО ОстаткиТоваров.Номенклатура = ОстаткиПартий.Номенклатура
| И ОстаткиТоваров.ХарактеристикаНоменклатуры = ОстаткиПартий.ХарактеристикаНоменклатуры
| И ОстаткиТоваров.Качество = ОстаткиПартий.Качество
| {ВНУТРЕННЕЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
| МестаХраненияНоменклатуры.Номенклатура КАК Номенклатура,
| СУММА(1) КАК ЧислоЯчеек
| ИЗ
| РегистрСведений.МестаХраненияНоменклатуры КАК МестаХраненияНоменклатуры
| ГДЕ
| МестаХраненияНоменклатуры.Склад = &Склад
| {ГДЕ
| МестаХраненияНоменклатуры.МестоХранения.*}
|
| СГРУППИРОВАТЬ ПО
| МестаХраненияНоменклатуры.Номенклатура) КАК ЯчейкиНоменклатуры
| ПО ОстаткиТоваров.Номенклатура = ЯчейкиНоменклатуры.Номенклатура}
|{ГДЕ
| ОстаткиТоваров.Номенклатура КАК Номенклатура,
| ОстаткиТоваров.Номенклатура.НоменклатурнаяГруппа КАК НоменклатурнаяГруппа}
|
|СГРУППИРОВАТЬ ПО
| ОстаткиТоваров.Номенклатура,
| ОстаткиТоваров.ХарактеристикаНоменклатуры," + ?(УчитыватьСерии, "
| ОстаткиТоваров.СерияНоменклатуры,", "") + "
| ОстаткиТоваров.Качество";
МассивОтбора = Новый Массив;
МассивОтбора.Добавить("Номенклатура");
МассивОтбора.Добавить("НоменклатурнаяГруппа");
МассивОтбора.Добавить("МестоХранения"); // Добавляем возможность отбора по ячейкам
УправлениеОтчетами.ЗаполнитьОтбор(МассивОтбора, ПостроительОтчета);
КонецПроцедуры
Важный момент: Мы добавили строку МассивОтбора.Добавить("МестоХранения"). Благодаря этому построитель автоматически выведет поле отбора на форму во вкладке настроек условий проведения инвентаризации.
Эта процедура отвечает за непосредственное заполнение табличной части документа физическими остатками. Нам нужно внедрить разработанный подзапрос в обе ветки алгоритма (для розничных складов НТТ и для оптовых складов). Найдем процедуру ЗаполнитьПоОстаткамНаСкладе(ПостроительОтчета, ТолькоУчетные = Ложь) и внесем изменения в тексты запросов.
Для розницы (блок Если Склад.ВидСклада = Перечисления.ВидыСкладов.НТТ Тогда) добавим соединение:
| {ВНУТРЕННЕЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
| МестаХраненияНоменклатуры.Номенклатура КАК Номенклатура,
| СУММА(1) КАК ЧислоЯчеек
| ИЗ
| РегистрСведений.МестаХраненияНоменклатуры КАК МестаХраненияНоменклатуры
| ГДЕ
| МестаХраненияНоменклатуры.Склад = &Склад
| {ГДЕ
| МестаХраненияНоменклатуры.МестоХранения.*}
|
| СГРУППИРОВАТЬ ПО
| МестаХраненияНоменклатуры.Номенклатура) КАК ЯчейкиНоменклатуры
| ПО ОстаткиТоваров.Номенклатура = ЯчейкиНоменклатуры.Номенклатура}
Аналогичное соединение необходимо вставить в секцию Иначе для оптовых и обычных розничных складов. Полный текст измененной процедуры вы можете увидеть в сообщении №28 обсуждения на форуме.
Функция перезаполнения учетных данных вызывается, когда инвентаризационная опись уже создана, но в процессе работы на складе изменились остатки, и их нужно актуализировать. Чтобы при этом из документа не исчезли товары, отфильтрованные по ячейкам, или наоборот, не добавился лишний ассортимент с других стеллажей, обязательно обновите запрос и в процедуре ПерезаполнитьУчетныеКоличества(ПостроительОтчета).
Внедрите блок {ВНУТРЕННЕЕ СОЕДИНЕНИЕ...} во все запросы этой процедуры перед секцией {ГДЕ ...} по аналогии с предыдущим шагом.
---
Если вы не имеете опыта программирования в среде 1С или ваша конфигурация находится на строгой поддержке (обновляется в автоматическом режиме), прямое вмешательство в код крайне нежелательно. Любая ошибка в синтаксисе запроса может парализовать проведение инвентаризаций на предприятии.
В этом случае отличной альтернативой станет создание **внешней обработки заполнения табличной части**.
Рассмотрим по шагам, как устроен этот механизм:
.epf.Инициализировать, принимающая в качестве параметра объект документа ИнвентаризацияТоваровНаСкладе.МестаХранения.МестаХраненияНоменклатуры по указанному стеллажу, и очистив табличную часть документа, переносит туда отфильтрованные строки.Преимущества использования внешней обработки:
---
Если вы все же выбрали Способ №1 и доработали запросы внутри конфигурации, вам потребуется вывести управление отбором для пользователей. В типовом решении УТ 10.3/УПП 1.3 на форме документа уже присутствует табличное поле отборов построителя отчета. Новое поле «Место хранения» появится там автоматически.
Если в вашей нетиповой (доработанной) форме отборы выведены в виде отдельных полей ввода, свяжите их напрямую со свойствами построителя отчета через палитру свойств элементов управления на форме:
ПостроительОтчета.Отбор.МестоХранения.Использование.ПостроительОтчета.Отбор.МестоХранения.ВидСравнения.ПостроительОтчета.Отбор.МестоХранения.Значение.Важное предупреждение: Перед настройкой элементов формы обязательно убедитесь, что выполнена процедура инициализации поля в модуле объекта (см. Шаг 1.1), иначе при открытии документа система выдаст критическую ошибку «Поле объекта не обнаружено (МестоХранения)» и закроет программу.