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