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