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

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

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

В данной статье мы подробно разберем, как реализовать такой функционал в конфигурации «Зарплата и управление персоналом 3.1» (ЗУП), хотя описанные методы универсальны для любой конфигурации на управляемых формах.

Метод 1. Поиск максимального номера с помощью запроса

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

Разберем алгоритм по шагам:

  1. Создаем команду на форме документа и выносим ее в виде кнопки.
  2. В обработчике команды вызываем серверную процедуру.
  3. В серверной процедуре выполняем запрос к базе данных.

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


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

    Запрос.УстановитьПараметр("Организация", Организация);
    Запрос.УстановитьПараметр("НачалоГода", НачалоГода(ДатаДокумента));
    Запрос.УстановитьПараметр("КонецГода", КонецГода(ДатаДокумента));

    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();

    Если Выборка.Следующий() И ЗначениеЗаполнено(Выборка.МаксНомер) Тогда
        // Извлекаем цифры, предполагая формат "У001"
        ТекстНомера = СокрЛП(Выборка.МаксНомер);
        ЧисловаяЧасть = Сред(ТекстНомера, 2); // Отсекаем префикс "У"
        НовыйНомерЧислом = Число(ЧисловаяЧасть) + 1;
    Иначе
        НовыйНомерЧислом = 1;
    EndЕсли;

    // Формируем строку с лидирующими нулями (например, У005)
    Возврат "У" + Формат(НовыйНомерЧислом, "ЧГ=0; ЧЦ=3; ЧВН=");
КонецФункции

В данном примере мы используем функцию МАКСИМУМ в языке запросов 1С. Важно помнить, что если поле строковое, то МАКСИМУМ может работать некорректно, если количество знаков в номере меняется (например, "У10" может оказаться меньше, чем "У9" при строковом сравнении). Поэтому крайне желательно использовать формат с лидирующими нулями: "У001", "У002" и так далее.

Метод 2. Использование независимого регистра сведений (Профессиональный подход)

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

Рассмотрим структуру такого регистра (назовем его СчетчикиНомеровУведомлений):

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

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


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

Метод 3. Работа с блокировками данных

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

Чтобы этого избежать, мы использовали объект БлокировкаДанных в предыдущем примере. Рассмотрим подробнее, как это работает. Когда первый пользователь инициирует процесс получения номера, система «бронирует» запись в регистре или определенный диапазон данных. Второй пользователь в этот момент переходит в состояние ожидания и получит доступ к коду только после того, как первый завершит транзакцию. Это единственный способ обеспечить 100% уникальность в многопользовательской среде.

Метод 4. Использование функций БСП

Поскольку ЗУП 3.1 построена на базе Библиотеки стандартных подсистем (БСП), мы можем использовать готовые инструменты для работы с префиксами и строками. Выясним, какие функции могут быть нам полезны.

Для работы с номерами в БСП часто используются общие модули, такие как ПрефиксацияОбъектовКлиентСервер или ПрефиксацияОбъектовСлужебный. Вместо того чтобы вручную обрезать строки через Сред() или Лев(), можно использовать встроенные механизмы, которые корректно обрабатывают префиксы организаций и информационных баз.

Пример использования БСП для дополнения номера нулями:

ПрефиксацияОбъектовКлиентСервер.ДополнитьСтрокуЛП(НовыйНомер, 3, "0")

Рекомендации по интеграции решения

Разберем, как лучше всего встроить этот функционал в типовую конфигурацию, чтобы не возникло проблем с обновлениями:

  1. Использование расширений: Никогда не меняйте типовой документ «Отпуск» напрямую. Создайте расширение конфигурации.
  2. Добавление реквизита: Если поле для номера уведомления отсутствует, добавьте его в расширение как новый реквизит документа.
  3. Изменение формы: Через расширение добавьте на форму документа кнопку. В свойствах кнопки укажите путь к команде.
  4. Проверки перед записью: Посмотрите, не заполнен ли номер уже. Проанализируйте ситуацию: если пользователь случайно нажмет кнопку дважды, он может перезаписать уже выданный номер. Добавьте проверку:
    
    Если ЗначениеЗаполнено(Объект.НомерУведомления) Тогда
        Ответ = Вопрос("Номер уже присвоен. Перезаписать?", РежимДиалогаВопрос.ДаНет);
        Если Ответ = КодВозвратаДиалога.Нет Тогда
            Возврат;
        КонецЕсли;
    КонецЕсли;
    

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

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

← На главную