Почему СрезПоследних в 1С выдает не тот документ при периодичности «По позиции регистратора»?

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

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

Суть проблемы: когда СрезПоследних «ошибается»

Рассмотрим типовую ситуацию: в течение одного дня один и тот же товар поступает по разным ценам несколькими документами. Программист, желая упростить структуру или следуя специфическим требованиям, устанавливает период записи в регистр принудительно на начало дня (00:00:00), несмотря на то, что сами документы-регистраторы разнесены по времени в течение дня. Логика разработчика проста: «Раз установлена периодичность по позиции регистратора, система сама поймет, какой документ был последним».

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

Разберем понятие «Момент времени»

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

  1. Дата (Период): В нашем примере это начало дня.
  2. Ссылка на регистратор: Уникальный идентификатор документа (GUID).

Когда мы выполняем запрос к СрезПоследних, система на уровне СУБД выполняет сортировку. Если периоды равны, сортировка происходит по бинарному представлению ссылки (GUID). Вот здесь и кроется главная ловушка.

Почему GUID — это не время?

Проанализируем ситуацию с точки зрения базы данных. Многие полагают, что GUID (уникальный идентификатор) создается последовательно: чем позже создан документ, тем «больше» его ссылка. Это заблуждение.

Уникальные идентификаторы в 1С могут генерироваться разными способами в зависимости от версии платформы, настроек сервера и способа создания объекта (в коде или интерактивно). В большинстве случаев GUID — это не монотонно возрастающая последовательность. Кроме того, при сравнении ссылок в SQL-запросе используется их бинарное представление, которое после определенных манипуляций платформы для оптимизации индексов вообще перестает коррелировать с хронологией.

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

Как решить проблему и получить корректные данные?

Выяснив причину, рассмотрим варианты исправления этой архитектурной ошибки. Проанализируем три основных подхода.

Способ 1: Отказ от фиксации периода на начало дня

Это самое правильное и логичное решение. Если вам нужно различать последовательность документов внутри дня, не нужно программно изменять период записи на 00:00:00. Позвольте платформе использовать реальное время документа (дата и время).

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

Способ 2: Запрос к физической таблице вместо виртуальной

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

Рассмотрим пример построения такого запроса:


ВЫБРАТЬ
    Цены.Номенклатура,
    МАКСИМУМ(Цены.Регистратор.Дата) КАК РеальнаяДатаДокумента
ПОМЕСТИТЬ ВТ_МаксимальныеДаты
ИЗ
    РегистрСведений.ЦеныНоменклатуры КАК Цены
ГДЕ
    Цены.Период = &НужнаяДатаНачалоДня
ГРУППИРОВАТЬ ПО
    Цены.Номенклатура;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Цены.Номенклатура,
    Цены.Цена,
    Цены.Регистратор
ИЗ
    ВТ_МаксимальныеДаты КАК Даты
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК Цены
        ПО Даты.Номенклатура = Цены.Номенклатура
        И Даты.РеальнаяДатаДокумента = Цены.Регистратор.Дата

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

Способ 3: Использование дополнительных измерений

Если бизнес-логика требует возможности ввода нескольких цен в один «момент» и вы хотите иметь строгий контроль над тем, какая цена приоритетнее, можно добавить в регистр сведений измерение Приоритет или Порядок (тип Число).

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

Рекомендации по проектированию

  1. Не подменяйте период: Использование НачалоДня() для периодических регистров лишает платформу возможности штатно использовать индексы по времени. Это «убивает» встроенный механизм СрезПоследних.
  2. Помните про GUID: Никогда не полагайтесь на то, что ссылка нового документа всегда больше ссылки старого. Это актуально как для запросов, так и для перебора объектов в коде.
  3. Анализируйте типовые решения: В современных конфигурациях (например, УТ 11 или ERP) для цен часто используется не только период, но и сложная логика версионирования или специальные документы установки цен, которые записывают данные в одну и ту же секунду, но имеют четкие алгоритмы вытеснения.

Таким образом, мы выяснили, что «глюк» СрезаПоследних — это на самом деле штатное поведение платформы. Система действует строго по алгоритму: Период + Ссылка. И если мы сами сделали периоды одинаковыми, мы отдали управление случайному распределению GUID. Проектируйте структуру данных с учетом реальной хронологии событий, и механизмы 1С будут работать предсказуемо.

← На главную