К статье
Практический материал

Разработка и Конфигурация · 11 мин

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

Как получить последний документ в 1С: запрос с ПЕРВЫЕ 1 и сортировкой по дате, поиск по номеру, отбор по организации, типичные ошибки и примеры кода 1С 8.3.

Задача «получить последний документ» возникает в коде 1С постоянно: при автонумерации, при проверке последовательности проведения, при выгрузке данных и при поиске последнего изменения по контрагенту или организации. Способ решения зависит от того, что именно считается «последним»: документ с максимальной датой, документ с максимальным номером или последний проведённый документ в определённом отборе.

Самый надёжный и быстрый способ — запрос с конструкцией ВЫБРАТЬ ПЕРВЫЕ 1 и сортировкой по дате по убыванию. Он работает на уровне СУБД, не перебирает документы в цикле и корректно обрабатывает большие объёмы данных. Альтернативы — метод Выбрать() менеджера документа с обходом выборки и метод ПолучитьПоследнийДокумент в некоторых типовых механизмах нумерации — применяются в более узких сценариях.

Ниже разобраны рабочие варианты кода для 1С 8.3, условия выбора между ними, типичные ошибки сортировки и способы проверить, что найден именно тот документ, который нужен.

Что считать «последним» документом

Прежде чем писать код, определите критерий «последности». В 1С документ идентифицируется в хронологии сразу двумя полями: датой и моментом времени. Дата документа имеет точность до секунды, но несколько документов могут иметь одинаковую дату — тогда порядок между ними задаёт МоментВремени, который включает дату и ссылку как уникальный разрешитель коллизий.

Три типичные трактовки задачи:

  • Последний по времени создания/проведения — максимальная дата и момент времени. Это самый частый смысл запроса.
  • Последний по номеру — максимальный номер в рамках периода нумерации. Используется в механизмах автонумерации, но не отражает хронологию: номер мог быть присвоен вручную или изменён.
  • Последний проведённый с условием — например, последняя реализация по контрагенту или последний приходный ордер по складу. Здесь к сортировке добавляется отбор.

Основной способ: запрос с ПЕРВЫЕ 1 и сортировкой по убыванию

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

bsl
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| РеализацияТоваровУслуг.Ссылка КАК Ссылка,
| РеализацияТоваровУслуг.Дата КАК Дата,
| РеализацияТоваровУслуг.Номер КАК Номер
|ИЗ
| Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
|УПОРЯДОЧИТЬ ПО
| РеализацияТоваровУслуг.Дата УБЫВ,
| РеализацияТоваровУслуг.МоментВремени УБЫВ";

Результат = Запрос.Выполнить();
Если Не Результат.Пустой() Тогда
 Выборка = Результат.Выбрать();
 Выборка.Следующий();
 ПоследнийДокумент = Выборка.Ссылка;
КонецЕсли;

Обратите внимание на два момента. Во-первых, сортировка задаётся по двум полям: Дата УБЫВ и МоментВремени УБЫВ. Только даты недостаточно — при совпадающих датах порядок строк станет непредсказуемым, и «последним» может оказаться любой из документов с этой датой. Во-вторых, перед обращением к выборке проверяется Результат.Пустой(): если документов ещё нет, обращение к полям выборки вызовет ошибку.

Если нужен не сам документ, а только его дата или номер (например, для предложения следующего номера), выбирайте только эти поля — не тяните в запрос лишние реквизиты и не делайте Ссылка.ПолучитьОбъект() без необходимости: получение объекта читает всю запись целиком и заметно медленнее.

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

На практике почти всегда нужен не просто последний документ вообще, а последний в конкретном разрезе. Условие добавляется секцией ГДЕ до упорядочивания. Типовой пример — последний проведённый документ по выбранной организации:

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

Условие Док.Проведен важно, если логика опирается на движения документа: помеченные на удаление и непроведённые документы не формируют записей в регистрах, и считать их «последними» для учётных задач обычно неверно. Если нужен последний документ независимо от проведения — уберите это условие, но сделайте это осознанно.

Для отбора по периоду добавьте условие на дату, например Док.Дата МЕЖДУ &НачалоПериода И &КонецПериода. Это типично при поиске последнего документа внутри месяца или квартала, когда документы следующего периода уже введены, но учитывать их нельзя.

Альтернативные способы: выборка менеджером и поиск по номеру

Обход выборки через Выбрать()

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

bsl
ПоследнийДокумент = Неопределено;
Выборка = Документы.ПриходныйКассовыйОрдер.Выбрать();
Пока Выборка.Следующий() Цикл
 ПоследнийДокумент = Выборка.Ссылка;
КонецЦикла;

Минус очевиден: платформа читает все документы вида, а не один. На базе с сотнями тысяч документов это заметная нагрузка. Используйте обход только там, где объём гарантированно мал, либо замените на запрос.

Поиск по максимальному номеру

Для задач автонумерации иногда требуется именно максимальный номер, чтобы предложить следующий. Здесь применяют запрос с агрегатной функцией МАКСИМУМ(Номер). Но помните об ограничении: номер хранится как строка, и максимум вычисляется по правилам строкового сравнения, а не числового. Кроме того, в типовых конфигурациях нумерацией управляют собственные механизмы (подписки на события, стратегии нумерации), и вмешиваться в них вручную следует только понимая их логику.

Сравнение способов

Запрос ПЕРВЫЕ 1 + сортировка УБЫВ

Оптимальный вариант: быстро, точно, поддерживает отборы. Подходит почти всегда.

Обход выборки Выбрать()

Простой код, но читает все документы. Допустим только на малых объёмах.

МАКСИМУМ(Номер)

Только для задач нумерации. Не отражает хронологию и зависит от строкового формата номера.

Как проверить, что найден правильный документ

После выполнения кода убедитесь, что результат соответствует ожиданиям. Самый простой контроль — вывести найденные дату, номер и ссылку в сообщение или журнал и сверить со списком документов в интерфейсе, отсортированном по дате. Последний документ в списке журнала должен совпадать с найденным в коде.

Контрольные точки

Как проверить результат

Совпадение с журналом документов
Норма

Найденная ссылка совпадает с верхним документом при сортировке журнала по дате по убыванию

Тревожный признак

В журнале есть документ позже найденного — значит, в запросе лишний отбор или неверная сортировка

Поведение при пустой таблице
Норма

Код корректно обрабатывает пустой результат (Неопределено, без исключения)

Тревожный признак

При отсутствии документов возникает ошибка обращения к выборке

Документы с одинаковой датой
Норма

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

Тревожный признак

Результат «прыгает» между документами с одинаковой датой от запуска к запуску

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

Типичные ошибки
01
Сортировка только по дате без момента времени — при совпадающих датах результат непредсказуем
02
Сортировка по номеру — строковые номера сравниваются посимвольно, «10» меньше «9»
03
Отсутствие проверки Пустой() — ошибка выполнения, когда документов ещё нет
04
Игнорирование пометки удаления — «последним» оказывается документ, которого фактически нет в учёте
05
Ссылка.ПолучитьОбъект() ради одного реквизита — лишнее чтение всей записи и падение производительности

Ограничения и границы применения

Запрос с ПЕРВЫЕ 1 возвращает данные на момент выполнения. Если документ используется для последующих расчётов в длительной транзакции, учитывайте, что другой пользователь может успеть создать более поздний документ — для критичных сценариев нумерации и последовательностей применяйте управляемые блокировки.

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

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

Итог

Что важно запомнить

Последний документ получают запросом ВЫБРАТЬ ПЕРВЫЕ 1 с сортировкой Дата УБЫВ, МоментВремени УБЫВ. Отборы по организации, контрагенту и признаку проведения добавляются через ГДЕ. Номер для хронологии не используют, пустой результат всегда проверяют, а найденную ссылку сверяют с журналом документов.

Частые вопросы

Как получить последний проведённый документ, игнорируя помеченные на удаление?

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

Почему запрос возвращает не тот документ, хотя сортировка по дате задана?

Наиболее вероятная причина — несколько документов с одинаковой датой и отсутствие второго поля сортировки. Добавьте МоментВремени УБЫВ после сортировки по дате. Вторая возможная причина — лишний отбор в секции ГДЕ, который исключает нужный документ, например условие по организации или по проведению.

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

Используйте соединение таблицы документов с самой собой через группировку: сгруппируйте по контрагенту с МАКСИМУМ(Дата), затем соедините результат с таблицей документов по контрагенту и дате, чтобы получить ссылку. Учтите, что при совпадающих датах у одного контрагента может вернуться несколько строк — тогда дополнительно сравнивайте момент времени.

Можно ли найти последний документ по номеру для предложения следующего номера?

Технически — через МАКСИМУМ(Номер), но это ненадёжно: номер строковый, сравнение идёт посимвольно, а префиксы узлов и организаций искажают результат. В типовых конфигурациях нумерация уже реализована платформенными механизмами и подписками на события — корректнее опираться на них, а не изобретать собственный расчёт номера.

Что быстрее: запрос с ПЕРВЫЕ 1 или обход выборки до конца?

Запрос с ПЕРВЫЕ 1 быстрее практически всегда: СУБД находит максимум по индексу и возвращает одну строку. Обход выборки читает все документы вида, и на больших базах разница измеряется секундами против миллисекунд. Обход оправдан только в учебных примерах или при гарантированно малом числе документов.