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

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

В практике разработки на платформе 1С:Предприятие 8.3 часто встречается задача получения наиболее актуальных данных из документов, которые могут иметь исправления. Типовой механизм исправления (реквизит ИсправленныйДокумент) позволяет создавать цепочки документов, где каждое последующее звено отменяет действие предыдущего. Ситуация усложняется, если документ является самописным и не делает движений по регистрам, на основании которых можно было бы получить срез последних данных. В такой ситуации единственный выход — грамотно выстроенный запрос к таблицам документов.

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

Постановка проблемы и анализ логики исключения

Рассмотрим ситуацию: у нас есть документ ИС_ПереводНаОплатуПоСреднемуЗаработкуСписком. В нем присутствует табличная часть Сотрудники с периодами действия (ДатаНачала, ДатаОкончания). Нам нужно найти документ, действующий на определенную дату по конкретному сотруднику, но при этом учесть, что если документ был исправлен, старая версия не должна попадать в отчет.

Основная ошибка начинающих разработчиков заключается в попытке наложить фильтр по дополнительным реквизитам (например, ВидОбразования) в самом начале выборки. Если в первичном документе был указан "Вид 1", а в исправлении его сменили на "Вид 2", то при поиске по "Виду 1" система может ошибочно выдать старый, уже неактуальный документ, так как она «не видит» исправления с другим значением реквизита. Выясним причину: это происходит потому, что условие ГДЕ отсекает исправляющий документ еще до того, как запрос проверит связь по полю ИсправленныйДокумент.

Решение через временные таблицы и подзапросы

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


ВЫБРАТЬ
    ТЧ.Ссылка КАК ПервичныйДокумент,
    ТЧ.Ссылка.ВидОбразования КАК ВидОбразования,
    ТЧ.Ссылка.ИсправленныйДокумент КАК ИсправленныйДокумент
ПОМЕСТИТЬ ПервичнаяВыборка
ИЗ
    Документ.ИС_ПереводНаОплатуПоСреднемуЗаработкуСписком.Сотрудники КАК ТЧ
ГДЕ
    ТЧ.ДатаНачала <= &Дата
    И ТЧ.ДатаОкончания >= &Дата
    И ТЧ.Сотрудник = &Сотрудник
    И ТЧ.Ссылка.Проведен = ИСТИНА;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Выборка.ПервичныйДокумент КАК НужныйДокумент,
    Выборка.ВидОбразования КАК ВидОбразования
ИЗ
    ПервичнаяВыборка КАК Выборка
ГДЕ
    НЕ Выборка.ПервичныйДокумент В
            (ВЫБРАТЬ
                П.ИсправленныйДокумент
            ИЗ
                ПервичнаяВыборка КАК П)
    И Выборка.ВидОбразования = &ВидОбразования

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

Оптимизация и работа со списочными документами

Если документов в базе много, использование оператора В (ВЫБРАТЬ...) может быть неоптимальным. Рассмотрим вариант с использованием ЛЕВОЕ СОЕДИНЕНИЕ, который в большинстве СУБД отрабатывает быстрее. Также разберем проблему «пустых дат», возникающую при обращении к табличным частям через точку.

Никогда не обращайтесь к данным табличной части через ссылку в условии ГДЕ (например, Ссылка.Сотрудники.Сотрудник = &Сотрудник). Это приводит к некорректному соединению таблиц на уровне SQL и может возвращать пустые значения. Всегда соединяйтесь с физической таблицей табличной части.

Посмотрим на оптимизированный пример с использованием индексации:


ВЫБРАТЬ
    ТЧ.Ссылка КАК Ссылка,
    ТЧ.Ссылка.ВидОбразования КАК ВидОбразования,
    ТЧ.Ссылка.ИсправленныйДокумент КАК ИсправленныйДокумент
ПОМЕСТИТЬ ВТ_ВсеДокументы
ИЗ
    Документ.ИС_ПереводНаОплатуПоСреднемуЗаработкуСписком.Сотрудники КАК ТЧ
ГДЕ
    ТЧ.Сотрудник = &Сотрудник
    И ТЧ.ДатаНачала <= &Дата
    И ТЧ.ДатаОкончания >= &Дата
    И ТЧ.Ссылка.Проведен;

ИНДЕКСИРОВАТЬ ПО
    Ссылка,
    ИсправленныйДокумент;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Док.Ссылка КАК АктуальныйДокумент
ИЗ
    ВТ_ВсеДокументы КАК Док
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВсеДокументы КАК Исправления
        ПО Док.Ссылка = Исправления.ИсправленныйДокумент
ГДЕ
    Исправления.Ссылка ЕСТЬ NULL
    И Док.ВидОбразования = &ВидОбразования

В данном примере мы используем ЛЕВОЕ СОЕДИНЕНИЕ таблицы саму на себя по условию Док.Ссылка = Исправления.ИсправленныйДокумент. Если для строки из левой таблицы не нашлось соответствия в правой (Исправления.Ссылка ЕСТЬ NULL), значит, этот документ является последним в цепочке (его никто не исправлял). Добавление ИНДЕКСИРОВАТЬ ПО существенно ускоряет выполнение запроса на больших объемах данных за счет создания временных индексов в базе tempdb.

Особенности цепочек исправлений и сторнирования

Проанализируем ситуацию, когда цепочка исправлений длиннее двух документов (А -> Б -> В). Описанные выше алгоритмы успешно справляются с этой задачей, так как условие Исправления.Ссылка ЕСТЬ NULL всегда оставит только "хвост" цепочки. Однако стоит помнить о следующих правилах:

  1. Статус проведения: При поиске исправлений всегда проверяйте Ссылка.Проведен = ИСТИНА. Если документ-исправление был создан, но не проведен или помечен на удаление, он не должен отменять действие первичного документа.
  2. Регистры-подсказки: Если ваша конфигурация основана на типовой (например, ЗУП 3.1), проверьте наличие регистров с постфиксом (испр.). Хотя автор темы указал, что его документ безрегистровый, в будущем рекомендуется добавить движения по регистрам сведений. Это позволит использовать стандартный СрезПоследних, что работает в разы быстрее любых сложных соединений в документах.
  3. Критерии отбора: Для программного поиска исправлений в коде (не в запросе) можно использовать метаданные КритерииОтбора.СвязанныеДокументы или аналогичные, если они настроены для вашего типа документа.

Подводя итог, можно сказать, что работа с исправлениями в 1С требует особого внимания к последовательности наложения фильтров. Сначала мы определяем актуальность документа в цепочке, и только потом проверяем его соответствие бизнес-условиям (вид образования, сумма, подразделение). Использование временных таблиц с индексацией и левых соединений является наиболее надежным и производительным способом решения данной задачи в рамках языка запросов.

← На главную