Почему не срабатывает контроль остатков при проведении документа и как исправить ошибку момента времени

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

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

Анализ механизма проведения по «новой методике»

Для начала разберем стратегию проведения, которую использовал автор темы. Эта методика (часто называемая методом Чистова) предполагает следующую последовательность действий:

  1. Запись движений: Документ сначала записывает свои движения в регистр накопления (флаг Записывать = Истина).
  2. Установка блокировок: Чтобы другие пользователи не изменили остатки в момент проверки, накладывается управляемая блокировка.
  3. Проверка остатков запросом: Выполняется запрос к виртуальной таблице Остатки, который должен «увидеть» движения самого документа и выявить наличие минусов.

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


// 3. Запись движений в БД
Движения.РегистрХранения.Записывать = Истина;
Движения.Записать(); // Записываем все наборы записей, у которых флаг Записывать = Истина

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

Проблема 1: Граница периода и включение движений документа

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

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

Проблема 2: Коварство типов данных во временных таблицах

Наиболее сложная для отладки ошибка — это несоответствие типов при использовании временных таблиц в запросе. Проанализируем ситуацию: автор помещает данные документа во временную таблицу НоменклатураДокументРазборкаХранение, а затем использует её для фильтрации остатков в виртуальной таблице регистра.

Разберем, где скрывается подвох. В первом запросе (формирование временной таблицы) поле выбирается следующим образом:


ВЫБРАТЬ
    КОГДА Комплектация.ТипДвижения = &Разборка
    ТОГДА Комплектация.Номенклатура
КОНЕЦ КАК Штрихкод
ПОМЕСТИТЬ НоменклатураДокументРазборкаХранение

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

Рекомендация: Всегда проверяйте типы полей во временных таблицах. Если в регистре измерение является строкой, то и во временную таблицу нужно передавать строку (например, Номенклатура.Код или Номенклатура.Наименование), а не саму ссылку.

Оптимизация записи движений

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


Движения.РегистрХранения.Записать();
Движения.СпецификацияНоменклатуры.Записать();

Это не является ошибкой, но порождает лишние обращения к серверу БД. Правильнее установить флаги записи и вызвать общий метод Записать() у объекта Движения:


Движения.РегистрХранения.Записывать = Истина;
Движения.СпецификацияНоменклатуры.Записывать = Истина;
Движения.Записать(); // Платформа запишет оба регистра в одной транзакции

Пошаговый чек-лист для исправления ошибки контроля остатков

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

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

← На главную