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