В процессе разработки на платформе 1С перед программистом часто встает задача получения кода самого «свежего» элемента справочника. Это может потребоваться для реализации собственной логики нумерации, формирования артикулов или автоматического заполнения связанных полей. Однако за простой фразой «последний созданный» скрывается несколько технических нюансов: от особенностей сортировки в базе данных до проблем одновременной работы нескольких пользователей.
Разберем детально, как решить эту задачу, начиная от простых запросов и заканчивая профессиональными методами обеспечения целостности данных.
Самый очевидный способ, который часто применяется новичками, — это выборка данных с сортировкой в обратном порядке. Проанализируем ситуацию: если мы предполагаем, что коды присваиваются последовательно и не редактируются вручную, то последний созданный элемент будет иметь максимальный код.
Рассмотрим пример кода, реализующий этот подход:
НашЗапрос = Новый Запрос;
НашЗапрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Номенклатура.Код КАК Код
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|УПОРЯДОЧИТЬ ПО
| Номенклатура.Код УБЫВ";
РезультатЗапроса = НашЗапрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Если Выборка.Следующий() Тогда
// Обработка полученного кода
ПоследнийКод = Выборка.Код;
// Пример инкремента (увеличения) кода
ЧисловойКод = Число(ПоследнийКод) + 1;
НовыйКод = Формат(ЧисловойКод, "ЧЦ=8; ЧВН=; ЧГ=0");
КонецЕсли;
Важное ограничение: Этот метод на самом деле ищет не «последний созданный» элемент, а элемент с максимальным кодом. Если пользователь вручную создаст элемент с кодом "9999", а затем система автоматически создаст элемент "0001", то запрос всегда будет возвращать "9999". Кроме того, данный подход крайне опасен в многопользовательской среде без использования блокировок.
Для того чтобы гарантированно определить последний созданный элемент по времени, а не по значению кода, рекомендуется добавить в справочник специальный реквизит, например, ДатаСоздания (тип Дата, состав даты — ДатаВремя).
Выясним причину, почему это лучше: стандартные поля 1С не всегда хранят точное время записи в удобном для запросов виде. Добавив свой реквизит и установив для него свойство Индексировать, мы получим возможность выполнять мгновенные запросы.
Алгоритм действий следующий:
ДатаСоздания в нужный справочник.ПередЗаписью добавьте код, заполняющий эту дату при создании нового элемента.
// В модуле объекта
Процедура ПередЗаписью(Отказ)
Если ЭтоНовый() Тогда
ДатаСоздания = ТекущаяДатаСеанса();
КонецЕсли;
КонецПроцедуры;
// Код для получения последнего элемента
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Ссылка,
| Код
|ИЗ
| Справочник.Номенклатура
|УПОРЯДОЧИТЬ ПО
| ДатаСоздания УБЫВ";
Если конфигурацию нельзя менять (например, она находится на поддержке и добавление реквизитов нежелательно), можно обратиться к системному Журналу регистрации. Платформа фиксирует каждое событие создания объекта.
Посмотрим на пример того, как можно извлечь данные из журнала:
Фильтр = Новый Структура;
Фильтр.Вставить("Событие", "_$Data$_.New"); // Событие создания нового объекта
Фильтр.Вставить("Метаданные", Метаданные.Справочники.Номенклатура);
ТаблицаЖурнала = Новый ТаблицаЗначений;
СкопироватьЖурналРегистрации(ТаблицаЖурнала, Фильтр, "Период, Данные", , 1); // Берем только последнюю запись
Если ТаблицаЖурнала.Количество() > 0 Тогда
ПоследнийОбъект = ТаблицаЖурнала[0].Данные;
ПоследнийКод = ПоследнийОбъект.Код;
КонецЕсли;
Внимание: Метод СкопироватьЖурналРегистрации может работать медленно на больших базах. Его не рекомендуется использовать в транзакциях записи или в высоконагруженных формах.
Некоторые разработчики пытаются использовать поле Ссылка для сортировки. В 1С 8.3 уникальный идентификатор (GUID) генерируется таким образом, что он содержит в себе временную составляющую. Однако полагаться на это нельзя по следующим причинам:
Проанализируем ситуацию: два пользователя одновременно нажимают кнопку «Создать». 1. Первый пользователь выполняет запрос, видит последний код "001". 2. Второй пользователь выполняет запрос, тоже видит последний код "001". 3. Оба прибавляют единицу и пытаются записать код "002". 4. Один из них получит ошибку «Код не уникален».
Чтобы избежать этой ошибки (шанс которой в нагруженных системах далеко не 1%, как упоминалось на форуме), необходимо использовать Управляемые блокировки. Перед тем как искать «последний код» для генерации нового, нужно заблокировать «пространство» этого справочника.
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Справочник.Номенклатура");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
// Теперь, внутри транзакции, мы можем спокойно искать последний код
// Другие пользователи будут ждать завершения нашей транзакции
Если ваша задача — просто обеспечить уникальную нумерацию, то лучше всего вообще отказаться от ручного поиска последнего кода. Платформа 1С делает это автоматически и гораздо эффективнее. Используйте событие ПриУстановкеНовогоКода в модуле объекта. Это штатное место для управления логикой кодирования, где платформа сама гарантирует получение корректного следующего номера.
Подведем итог: если вам действительно нужно знать, какой элемент был создан последним по времени, используйте дополнительный индексированный реквизит ДатаСоздания. Если же ваша цель — генерация нового кода, доверьтесь встроенным механизмам 1С и событию ПриУстановкеНовогоКода, дополняя его управляемыми блокировками в критических узлах системы.