Как получить остатки по каждому регистратору в 1С и найти минимальное значение за период

Программист 1С v8.3 (Обычные формы) Управленческий учет
← На главную

При разработке учетных систем на платформе 1С часто возникает задача контроля остатков при редактировании документов "задним числом". Представим ситуацию: пользователь хочет уменьшить количество в старом документе поступления. Чтобы понять, допустима ли такая правка, нам необходимо проанализировать всю цепочку последующих движений по этой номенклатуре. Если в какой-то момент после этого поступления остаток опускался до критического минимума, то мы не можем уменьшить приход больше, чем на это минимальное значение, иначе в будущем возникнет отрицательный остаток («краснота»).

В этой статье мы подробно рассмотрим, как с помощью языка запросов 1С получить остатки после каждого регистратора и эффективно вычислить «запас прочности» — минимальный остаток за период.

Метод 1. Использование виртуальной таблицы ОстаткиИОбороты

Самый простой и логичный способ решить задачу — обратиться к виртуальной таблице ОстаткиИОбороты регистра накопления. Ключевым моментом здесь является параметр Периодичность. Если установить его в значение Регистратор, платформа самостоятельно рассчитает итоги на момент каждого документа, сделавшего движение по регистру.

Проанализируем пример запроса, который позволяет получить конечные остатки после каждого документа:


ВЫБРАТЬ
    ТоварыНаСкладахОстаткиИОбороты.Регистратор КАК Регистратор,
    ТоварыНаСкладахОстаткиИОбороты.Период КАК Период,
    ТоварыНаСкладахОстаткиИОбороты.КоличествоКонечныйОстаток КАК КонечныйОстаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Регистратор, , Номенклатура = &Номенклатура) КАК ТоварыНаСкладахОстаткиИОбороты
УПОРЯДОЧИТЬ ПО
    Период,
    Регистратор.МоментВремени

Разберем, почему это работает. Параметр Регистратор заставляет систему вычислять итоги не за весь период сразу, а пошагово. В результате мы получаем таблицу, где каждая строка соответствует документу-движению, а поле КоличествоКонечныйОстаток показывает состояние склада сразу после проведения этого документа.

Метод 2. Поиск минимального остатка для определения лимита правки

Чтобы решить задачу автора темы — найти, на сколько максимально можно уменьшить поступление — нам нужно найти минимальное значение из всех полученных конечных остатков. Рассмотрим, как модифицировать запрос, чтобы сразу получить это число:


ВЫБРАТЬ ПЕРВЫЕ 1
    ТоварыНаСкладахОстаткиИОбороты.КоличествоКонечныйОстаток КАК МинимальныйОстаток,
    ТоварыНаСкладахОстаткиИОбороты.Регистратор КАК КритическийДокумент
ИЗ
    РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&ДатаПоступления, &ТекущаяДата, Регистратор, , Номенклатура = &Номенклатура И Склад = &Склад) КАК ТоварыНаСкладахОстаткиИОбороты
ГДЕ
    ТоварыНаСкладахОстаткиИОбороты.КоличествоКонечныйОстаток >= 0
УПОРЯДОЧИТЬ ПО
    ТоварыНаСкладахОстаткиИОбороты.КоличествоКонечныйОстаток ВОЗР

Важные замечания по этому решению:

  1. Период: Мы начинаем проверку от даты изменяемого документа (&ДатаПоступления) до текущего момента. Проверять данные до поступления нет смысла, так как изменение текущего документа на них не повлияет.
  2. Сортировка: Используя УПОРЯДОЧИТЬ ПО ... ВОЗР и ВЫБРАТЬ ПЕРВЫЕ 1, мы мгновенно находим ту самую «узкую точку» в истории движений товара.
  3. Лимит: Если МинимальныйОстаток равен 5, это значит, что мы можем уменьшить наше поступление максимум на 5 единиц. Если пользователь пытается уменьшить на 6, программа должна выдать отказ.

Метод 3. Проверка «выхода в минус» при проведении

Если задача стоит более узко — просто не дать провести документ, если после него где-то в будущем возникнет минус — можно использовать логику «от противного». Мы ищем любой регистратор, после которого остаток стал меньше нуля после нашего гипотетического изменения.


ВЫБРАТЬ
    ТоварыНаСкладахОстаткиИОбороты.Регистратор,
    ТоварыНаСкладахОстаткиИОбороты.КоличествоКонечныйОстаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&ДатаДокумента, , Регистратор, , Номенклатура = &Номенклатура) КАК ТоварыНаСкладахОстаткиИОбороты
ГДЕ
    ТоварыНаСкладахОстаткиИОбороты.КоличествоКонечныйОстаток < 0

Если такой запрос возвращает хотя бы одну строку, значит, изменение документа привело к дефициту товара в будущем периоде.

Оптимизация производительности: Физические таблицы vs Виртуальные

Рассмотрим ситуацию, когда база данных очень большая. Использование виртуальной таблицы ОстаткиИОбороты с периодичностью Регистратор может работать медленно, так как системе приходится выполнять сложные вложенные запросы для расчета остатка на каждую точку времени.

В высоконагруженных системах опытные программисты рекомендуют альтернативный подход:

  1. Получаем текущий остаток на момент ДО изменяемого документа (используя виртуальную таблицу Остатки).
  2. Выбираем все физические движения (таблица РегистрНакопления.ИмяРегистра) после этого момента, отфильтровав их по нужной номенклатуре.
  3. В коде на языке 1С или с помощью сложного запроса с соединением (Self-Join) вычисляем нарастающий итог.

Для большинства типовых задач достаточно виртуальной таблицы, но если вы замечаете задержки при записи документа, проанализируйте возможность перехода на работу с физической таблицей движений.

Тонкости работы с Моментом Времени

Выясним важную причину, по которой нельзя сортировать движения только по дате. В 1С в одну и ту же секунду может быть записано несколько документов. Если мы будем ориентироваться только на время, порядок документов в запросе может оказаться случайным, что приведет к неверному расчету остатка «после регистратора».

Всегда используйте в запросах и при получении границ объект МоментВремени. В языке запросов это реализуется сортировкой по полям Период и Ссылка. Именно Ссылка (GUID) гарантирует однозначную последовательность документов внутри одной секунды.

Также при установке параметров виртуальной таблицы используйте объект Граница:

Новый Граница(ДокументСсылка.МоментВремени(), ВидГраницы.Исключая) — чтобы получить остаток строго ДО текущего документа.

Блокировки данных для предотвращения ошибок

Проанализируем ситуацию: два пользователя одновременно решили изменить документы по одному и тому же товару. Если оба запроса на расчет минимального остатка выполнятся одновременно, они могут оба получить «добро», но после записи обоих документов остаток уйдет в минус.

Чтобы этого избежать, расчет минимального остатка всегда должен выполняться внутри транзакции проведения с установкой управляемой блокировки на комбинацию Номенклатура + Склад. Используйте объект БлокировкаДанных перед выполнением запроса:


Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ТоварыНаСкладах");
ЭлементБлокировки.УстановитьЗначение("Номенклатура", ВыбраннаяНоменклатура);
ЭлементБлокировки.УстановитьЗначение("Склад", ВыбранныйСклад);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
// Теперь можно выполнять запрос и проверять остатки

Это гарантирует, что пока вы проверяете «запас прочности» товара для одного документа, никто другой не сможет изменить состояние этого же товара на складе.

← На главную