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

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

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

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

Причина проблемы: почему документ «видит» сам себя

Когда мы вызываем метод Остатки() виртуальной таблицы регистра в процедуре ОбработкаПроведения, система обращается к данным, которые уже физически записаны в базе данных. Если документ перепроводится, его записи за предыдущее проведение всё еще находятся в регистре. В результате получается, что документ «опирается» на свои же старые данные, что приводит к логическим ошибкам при проверке лимитов.

Решение 1: Очистка движений перед расчетом (Классическая методика)

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

Проанализируем шаги реализации в процедуре ОбработкаПроведения:


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

Важный нюанс: Обратите внимание на использование объекта Граница с параметром ВидГраницы.Исключая. Это позволяет получить остатки строго до текущего документа на ту же секунду времени.

Решение 2: Проверка остатков «на будущее»

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

Чтобы реализовать такую проверку, выясним причину: почему бы не указывать период вообще? Если в параметрах виртуальной таблицы Остатки оставить поле периода пустым, 1С вернет актуальный остаток на самую последнюю дату в системе (3999 год). Это и будет проверкой «после всех документов».

Посмотрим, как это выглядит в коде:


// Получаем остаток на "сейчас" (совсем без привязки к дате документа)
ОстаткиТовара = РегистрыБухгалтерии.Хозрасчетный.Остатки( , ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Номенклатура, ОтборНоменклатуры, , "Количество");

Однако здесь кроется ловушка: если документ уже проведен, этот «финальный» остаток включает в себя движения текущего документа. Чтобы получить корректный результат, мы можем использовать математический метод: Результат = ТекущийОстатокВБазе + СтароеДвижениеДокумента - НовоеСписание.

Решение 3: Использование событий набора записей (Универсальный контроль)

Рассмотрим более сложный, но эффективный метод, предложенный в сообщении №4. Этот метод заключается в переносе логики контроля в модуль набора записей регистра. Это позволяет контролировать остатки централизованно для всех документов сразу.

Разберем алгоритм работы в процедуре ПередЗаписью модуля набора записей:

  1. Проверяем, является ли запись списанием (Расход).
  2. Формируем временную таблицу из текущих записей документа, которые еще только готовятся к записи.
  3. Выполняем запрос к таблице Остатки регистра, используя Границу исключая момент времени.
  4. Сравниваем планируемый расход с имеющимся остатком.

Пример реализации функции контроля:


Процедура КонтрольОтрицательныхОстатков(Отказ, НаборЗаписей)
    // Подготавливаем запрос для анализа остатков
    Запрос = Новый Запрос;
    // Период устанавливаем как Границу ДО текущего документа
    Запрос.УстановитьПараметр("Период", Новый Граница(НаборЗаписей[0].Период, ВидГраницы.Исключая));
    Запрос.УстановитьПараметр("НЗ", НаборЗаписей.Выгрузить());
    
    // Текст запроса должен соединять данные Набора Записей (НЗ) 
    // с виртуальной таблицей Остатки и проверять условие: Т.Количество > Остатки.КоличествоОстаток
    // ... (полный текст запроса представлен в сообщении №4 обсуждения)
КонецПроцедуры

Решение 4: Современная методика (Контроль после записи)

Проанализируем современный стандарт разработки (используемый в БП 3.0 и ERP). Вместо того чтобы пытаться «не видеть» свои движения, мы, наоборот, сначала записываем новые движения в базу, а затем одним запросом проверяем — не ушли ли остатки в минус во всей базе с учетом наших новых данных.

Этапы выполнения:

  1. В начале ОбработкаПроведения устанавливаем Движения.Хозрасчетный.Записывать = Истина;.
  2. Формируем движения (наполняем коллекции).
  3. Вызываем Движения.Записать(); — теперь в базе лежат актуальные данные.
  4. Выполняем запрос к остаткам на пустую дату (на конец времен) с фильтром по номенклатуре из документа.
  5. Если где-то видим минус — устанавливаем Отказ = Истина.

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

Дополнительные советы для профессиональной разработки

Рассмотрим несколько важных аспектов, которые помогут сделать ваш код надежным:

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

← На главную