Как при проведении документа в запросе остатков учесть его собственные движения?

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

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

Почему запрос не видит движения текущего документа?

Когда мы работаем внутри процедуры ОбработкаПроведения, вся работа выполняется в рамках одной транзакции. Платформа 1С устроена таким образом, что движения, которые мы добавляем в коллекцию Движения, физически не записываются в базу данных (в таблицы СУБД) до тех пор, пока не будет вызван явный метод записи или пока транзакция не завершится успешно. Следовательно, когда мы выполняем запрос к виртуальной таблице Остатки, СУБД считывает данные из таблиц итогов и таблицы движений, где записей нашего документа еще просто нет — они находятся в оперативной памяти в виде коллекции объектов.

Решение: Явная запись движений

Для того чтобы запрос смог «увидеть» движения, которые формирует текущий документ, их необходимо принудительно записать в базу данных до момента выполнения запроса. Рассмотрим, как это сделать правильно. Мы используем метод Записать() для конкретного набора записей из коллекции движений документа.

Проанализируем пример кода:


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    
    // 1. Формируем движения документа
    Движение = Движения.ВзаиморасчетыСКонтрагентами.Добавить();
    Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
    Движение.Период = Дата;
    Движение.Контрагент = Контрагент;
    Движение.СуммаУпр = СуммаДокумента;
    
    // 2. Явно записываем движения в базу данных внутри транзакции
    Движения.ВзаиморасчетыСКонтрагентами.Записать();
    
    // 3. Теперь запрос увидит эти данные
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ВзаиморасчетыОстатки.СуммаУпрОстаток КАК Сумма
        |ИЗ
        |   РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки(&МоментВремени, Контрагент = &Контрагент) КАК ВзаиморасчетыОстатки";
    
    // Важно правильно установить МоментВремени с границей "Включая"
    ГраницаКонтроля = Новый Граница(МоментВремени(), ВидГраницы.Включая);
    Запрос.УстановитьПараметр("МоментВремени", ГраницаКонтроля);
    Запрос.УстановитьПараметр("Контрагент", Контрагент);
    
    РезультатЗапроса = Запрос.Выполнить();
    // Далее следует анализ остатков...
    
КонецПроцедуры

Использование объекта Граница

Обратите внимание на использование объекта Граница. Если мы передадим в параметры запроса просто МоментВремени(), система может выдать остатки «на начало» позиции документа, не включая движения самого документа, даже если они уже записаны в базу. Использование ВидГраницы.Включая гарантирует, что точка среза остатков пройдет после нашего документа, захватив все его записи.

«Старая» и «Новая» методики проведения: в чем разница?

Обсуждение вопроса учета движений неизбежно подводит нас к выбору методики контроля остатков. В среде 1С принято выделять два подхода:

  1. Старая методика (Контроль до записи):

    Сначала мы блокируем нужные ресурсы (регистры), затем считываем текущие остатки, проверяем, хватает ли их для проведения документа, и только если всё в порядке — формируем и записываем движения. Минус: Увеличивается время блокировки регистра, так как блокировка ставится в самом начале процедуры и держится до конца транзакции.

  2. Новая методика (Запись, затем контроль):

    Мы сначала формируем движения и записываем их в базу (как показано в примере выше). Затем выполняем запрос, который проверяет, не ушли ли остатки в минус после наших действий. Если обнаруживается отрицательный остаток, мы устанавливаем Отказ = Истина, и транзакция откатывается. Плюс: Блокировка на запись ставится максимально поздно, что повышает параллельность работы пользователей.

Технические преимущества новой методики

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

Борьба с дедлоками (Deadlocks): В старой методике часто возникает ситуация, когда два пользователя сначала читают данные (Shared Lock), а затем пытаются их записать (Exclusive Lock). Если они читают одни и те же записи, возникает взаимная блокировка. В новой методике мы сразу вызываем Записать(), устанавливая исключительную блокировку. Другие пользователи просто встают в очередь, и дедлока не происходит.

Оптимизация работы с СУБД: При использовании новой методики рекомендуется проверять остатки не точечно, а целым пакетом. Проанализируем правильный алгоритм:

Нюанс с флагом «Записывать модифицированные»

Посмотрим на свойство документа Запись движений. Если установлено значение Записывать модифицированные, платформа автоматически записывает те наборы движений, которые были изменены. Однако, если вы вызвали Движения.Записать() вручную в середине кода, платформа пометит этот набор как «немодифицированный». Если вы после этого решите что-то дописать в этот же регистр, не забудьте снова вызвать Записать() или убедитесь, что флаг модифицированности установлен, иначе повторная автоматическая запись в конце процедуры может не сработать или сработать не так, как ожидалось.

Ресурсы и производительность

Рассмотрим ситуацию, когда отказ в проведении из-за нехватки остатков случается часто. В этом случае новая методика может оказаться чуть более затратной, так как операция ROLLBACK (откат транзакции) в СУБД — это тяжелая операция, требующая работы с логом транзакций. Однако в 99% бизнес-сценариев проведение документа — это успешная операция, и выигрыш от сокращения времени блокировок при записи значительно перевешивает затраты на редкие откаты.

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

← На главную