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

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

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

Главный архитектурный принцип: Разделение печати и учета

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

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

Если генерировать номер в момент печати, мы столкнемся со следующими проблемами:

  1. Пропуски номеров: Пользователь открыл печатную форму (номер сгенерировался и записался как использованный), но передумал печатать и закрыл форму. Номер "сгорел".
  2. Дублирование или некорректное изменение номеров: Пользователь распечатал документ дважды. При повторном нажатии кнопки система может сгенерировать следующий по порядку номер, хотя договор фактически один и тот же.

Правильное решение: Сквозной номер должен присваиваться самому объекту базы данных (документу или элементу справочника "Договоры контрагентов") при его непосредственной записи или проведении в базе данных (в обработчике события ПередЗаписью). Печатная форма должна выполнять исключительно роль отображения данных и лишь считывать уже сохраненный в реквизите объекта номер.

Вариант 1. Использование штатных "Нумераторов" в 1С

Если сквозная нумерация требуется для нескольких различных видов документов (например, "Договор услуг", "Договор поставки", "Дополнительное соглашение"), платформа 1С предоставляет готовый объект метаданных — Нумераторы.

Разберем по шагам, как настроить этот механизм:

  1. В конфигураторе 1С перейдем в ветку "Документы" и найдем раздел "Нумераторы".
  2. Создадим новый нумератор, например, НумераторДоговоров.
  3. В свойствах нумератора зададим периодичность (например, "В пределах года") и установим флаг "Контроль уникальности".
  4. В свойствах каждого из документов, которые должны иметь сквозную нумерацию, в поле "Нумератор" укажем созданный нами НумераторДоговоров.

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

Вариант 2. Использование встроенных механизмов автоматической нумерации договоров

Если мы работаем в современных типовых конфигурациях (например, "1С:Бухгалтерия 8" ред. 3.0, "1С:ERP" или "1С:Комплексная автоматизация"), нам не обязательно писать программный код для нумерации договоров контрагентов. Рассмотрим, как включить эту опцию в пользовательском режиме:

  1. Перейдем в раздел "Администрирование" (или "Параметры учета").
  2. Найдем настройки нумерации договоров.
  3. Включим флаг автоматического присвоения номеров договорам.

Система самостоятельно настроит нумерацию справочника "Договоры контрагентов" по порядку в рамках календарного года. При создании нового договора поле "Номер" будет автоматически заполнено очередным свободным значением при записи.

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

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

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

Создадим независимый регистр сведений СчетчикНомеровДоговоров со следующей структурой:

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


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

Этот алгоритм на 100% страхует систему от появления дублирующих номеров. Блокировка накладывается исключительно на конкретную Организацию, что позволяет пользователям других филиалов беспрепятственно записывать свои документы параллельно.

Вариант 4. Красивый вывод номера на печатную форму

В базе данных 1С номера часто хранятся в строковом формате со служебными префиксами и лидирующими нулями (например, МСК00-000412). Выводить такой системный номер в печатную форму договора клиенту неэстетично.

Для приведения номера к человекочитаемому виду воспользуемся стандартной функцией из Библиотеки стандартных подсистем (БСП):


// Пример вывода номера в макет печатной формы
НомерНаПечать = ПрефиксацияОбъектовКлиентСервер.ПолучитьНомерНаПечать(ЭтотОбъект.Номер, Истина, Истина);
// Результат выполнения функции для номера "МСК00-000412" будет: "412"

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

Вариант 5. Особенности нумерации дополнительных соглашений

Специфика учета договоров часто требует ведения нумерации дополнительных соглашений, жестко привязанных к основному договору. В таких случаях используется формат номера [НомерДоговора]/[ПорядковыйНомерДопСоглашения] (например, 412/1, 412/2).

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


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

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

← На главную