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