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

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

При разработке отчетов и обработок в 1С часто возникает задача определить документ поступления, предшествующий документу реализации. Ситуация усложняется, когда пользователи проводят документы «задним числом» на одну и ту же дату и секунду (например, 02.12.2016 23:59:59). В этом случае стандартный запрос с условием Период <= &Дата и сортировкой по убыванию периода может вернуть некорректный результат или не найти нужную запись.

Разберем причины возникновения этой проблемы и рассмотрим надежные способы получения правильного регистратора через язык запросов 1С.

Почему стандартный запрос дает сбой при одинаковых секундах?

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

  1. Отсутствие объекта МоментВремени в языке запросов: тип МоментВремени существует только в объектной модели платформы 1С. В тексте запроса нельзя написать условие вида ТоварыНаСкладах.МоментВремени < &МоментВремени — СУБД оперирует исключительно полями таблиц базы данных.
  2. Недетерминированная сортировка: если два документа имеют одинаковое значение поля Период, условие УПОРЯДОЧИТЬ ПО Период УБЫВ не гарантирует строгий порядок строк. СУБД возвращает записи с одинаковым периодом в произвольном порядке, поэтому конструкция ВЫБРАТЬ ПЕРВЫЕ 1 может вернуть случайный документ.
  3. Неявные соединения составного типа: поле Регистратор имеет составной ссылочный тип. При обращении к реквизитам регистратора без ограничения типов платформа формирует неявные левые соединения (LEFT JOIN) со всеми таблицами документов, являющихся регистраторами регистра, что снижает производительность.

Способ 1. Эмуляция сравнения по моменту времени в реальной таблице регистра

Рассмотрим, как правильно сравнить положение документов на оси времени внутри текста запроса. Для этого сопоставим даты и ссылки документов, добавив полную детерминированную сортировку:


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

Запрос.УстановитьПараметр("Номенклатура", ВыбраннаяНоменклатура);
Запрос.УстановитьПараметр("ВидДвижения", ВидДвиженияНакопления.Приход);
Запрос.УстановитьПараметр("ДатаДокумента", ДокументСсылка.Дата);
Запрос.УстановитьПараметр("ТекущийРегистратор", ДокументСсылка);

РезультатЗапроса = Запрос.Выполнить();

Посмотрим на ключевые особенности этого подхода:

  1. Составное условие на дату и ссылку: конструкция ТоварыНаСкладах.Период = &ДатаДокумента И ТоварыНаСкладах.Регистратор < &ТекущийРегистратор позволяет однозначно отсечь записи, созданные текущим или более поздними документами в рамках той же секунды.
  2. Полный порядок сортировки: сортировка по полям Период, Регистратор и НомерСтроки обеспечивает строго предсказуемый результат работы инструкции ВЫБРАТЬ ПЕРВЫЕ 1.

Способ 2. Использование виртуальной таблицы «Обороты» и объекта «Граница»

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


ГраницаПоиска = Новый Граница(ДокументСсылка.МоментВремени(), ВидГраницы.Исключая);

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

Запрос.УстановитьПараметр("ПериодОкончания", ГраницаПоиска);
Запрос.УстановитьПараметр("Номенклатура", ВыбраннаяНоменклатура);

РезультатЗапроса = Запрос.Выполнить();

Проанализируем преимущества работы с виртуальной таблицей:

  1. Объект Граница: передача ВидГраницы.Исключая гарантирует, что платформа автоматически исключит движения самого документа расхода и любые движения, находящиеся позже на шкале времени.
  2. Фильтрация по приходу без перечисления типов: вместо проверки ВидДвижения или перечисления всех возможных типов документов (ПоступлениеТоваровУслуг, ВводНачальныхОстатков, ПрочееОприходованиеТоваров) достаточно наложить условие на ресурс оборота ВНаличииПриход > 0 (или КоличествоПриход > 0 в зависимости от конфигурации).
  3. Оптимизация производительности: отбор условий непосредственно в параметрах виртуальной таблицы существенно снижает объем читаемых данных из СУБД.

Рекомендации для типовых конфигураций (УТ 11, КА, ERP)

Обратите внимание: в конфигурациях линейки УТ 11 регистр ТоварыНаСкладах служит исключительно для учета складских остатков в штуках и не предназначен для партионного учета. Если задача состоит в сопоставлении списания и поступления по методу ФИФО или по конкретным поставкам, рекомендуется использовать специализированные регистры партионного учета (например, ДетализацияПартийТоваровДляНДС или ПартииТоваровНаСкладах), где партия фиксируется как отдельное измерение.

← На главную