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

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

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

Способ 1. Использование механизма подчинения владельцу

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

Для реализации этого метода выполним следующие шаги:

  1. Создадим вспомогательный справочник Года. В нем будут храниться элементы, соответствующие календарным годам (например, "2023", "2024").
  2. В целевом справочнике на закладке "Владельцы" укажем справочник Года.
  3. На закладке "Данные" установим свойство "Нумерация" в значение "В пределах подчинения владельцу".

Теперь при создании нового элемента нам достаточно программно определить текущий год и подставить соответствующего владельца. Рассмотрим пример кода в модуле объекта целевого справочника в обработчике ПередЗаписью:


Процедура ПередЗаписью(Отказ)
    Если ЭтоНовый() И НЕ ЗначениеЗаполнено(Владелец) Тогда
        ТекущийГодТекст = Формат(ТекущаяДата(), "ДФ=yyyy");
        СсылкаНаГод = Справочники.Года.НайтиПоНаименованию(ТекущийГодТекст);
        Если СсылкаНаГод.Пустая() Тогда
            // Если года еще нет в справочнике, создадим его
            НовыйГод = Справочники.Года.СоздатьЭлемент();
            НовыйГод.Наименование = ТекущийГодТекст;
            НовыйГод.Записать();
            СсылкаНаГод = НовыйГод.Ссылка;
        КонецЕсли;
        Владелец = СсылкаНаГод;
    КонецЕсли;
КонецПроцедуры

Этот метод хорош тем, что платформа сама контролирует уникальность и последовательность кодов. Однако он кардинально меняет структуру данных: справочник становится подчиненным, что может потребовать переработки существующих запросов и отчетов.

Способ 2. Использование префиксов и события ПриУстановкеНовогоКода

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

Проанализируем работу обработчика ПриУстановкеНовогоКода в модуле объекта справочника. Мы будем добавлять текущий год в начало кода в качестве префикса:


Процедура ПриУстановкеНовогоКода(СтандартнаяОбработка, Префикс)
    // Формируем префикс на основе текущего года, например "24-"
    ПрефиксГода = Формат(ТекущаяДата(), "ДФ=yy") + "-";
    Префикс = Префикс + ПрефиксГода;
КонецПроцедуры

Важный момент: Чтобы этот механизм работал корректно, убедитесь, что длина кода справочника достаточна для размещения и префикса, и самого номера. Если в 2024 году платформа встретит код "24-001", а в 2025 году мы установим префикс "25-", система не найдет кодов, начинающихся на "25-", и предложит номер "25-001".

Способ 3. Нумерация произвольного реквизита через регистр сведений

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

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

Создадим независимый регистр сведений СчетчикиНомеров с измерениями Год (Число) и ТипОбъекта (Строка), и ресурсом ПоследнийНомер (Число).

Разберем алгоритм получения номера в модуле объекта справочника:


Процедура ПередЗаписью(Отказ)
    Если ЭтоНовый() И ВнутреннийНомер = 0 Тогда
        ТекущийГод = Год(ТекущаяДата());
        
        // Устанавливаем блокировку для предотвращения дублей в многопользовательском режиме
        Блокировка = Новый БлокировкаДанных;
        ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.СчетчикиНомеров");
        ЭлементБлокировки.УстановитьПараметр("Год", ТекущийГод);
        ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
        Блокировка.Заблокировать();
        
        // Получаем текущее значение счетчика
        Отбор = Новый Структура("Год, ТипОбъекта", ТекущийГод, "МойСправочник");
        ЗначениеСчетчика = РегистрыСведений.СчетчикиНомеров.Получить(Отбор).ПоследнийНомер;
        
        НовыйНомер = ЗначениеСчетчика + 1;
        
        // Обновляем счетчик в регистре
        МенеджерЗаписи = РегистрыСведений.СчетчикиНомеров.СоздатьМенеджерЗаписи();
        МенеджерЗаписи.Год = ТекущийГод;
        МенеджерЗаписи.ТипОбъекта = "МойСправочник";
        МенеджерЗаписи.ПоследнийНомер = НовыйНомер;
        МенеджерЗаписи.Записать();
        
        ВнутреннийНомер = НовыйНомер;
    КонецЕсли;
КонецПроцедуры

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

Рассмотрим ситуацию, когда два бухгалтера одновременно нажимают кнопку "Записать". Без использования объекта БлокировкаДанных оба сеанса могут прочитать из базы одно и то же число (например, 100), прибавить к нему единицу и попытаться записать номер 101. Это приведет либо к ошибке уникальности, либо к появлению дублей, если контроль уникальности не настроен. Именно поэтому работа со счетчиками должна происходить внутри транзакции записи с использованием исключительных блокировок.

Посмотрим на проблему пропусков в нумерации. Платформенный механизм 1С "бронирует" номер при открытии формы или вызове метода УстановитьНовыйКод(). Если пользователь передумал и закрыл форму без записи, этот номер может быть потерян, и в последовательности возникнет "дырка". Если для вашей бизнес-логики критична непрерывная нумерация, присваивайте номер в самый последний момент — в событии ПередЗаписью, как показано в примерах выше.

Выводы и рекомендации

Проанализировав все варианты, можно сделать следующие выводы:

  1. Если допустимо изменение структуры, используйте подчинение справочнику "Года". Это самый надежный и простой в сопровождении путь, так как всю работу по генерации номеров берет на себя платформа.
  2. Если нужно оставить справочник в плоском виде, используйте префиксы с годом в событии ПриУстановкеНовогоКода. Это стандартный путь для большинства типовых конфигураций.
  3. Для произвольных реквизитов (не Кода) лучше всего подходит решение с использованием регистра сведений и обязательным применением управляемых блокировок.

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

← На главную