В отличие от конфигурации «1С:Бухгалтерия предприятия», где механизм автонумерации договоров доступен из коробки, в конфигурации «1С:Управление торговлей 11.4» (а также в 1С:ERP и 1С:Комплексная автоматизация) разработчики оставили заполнение номера и даты договоров на усмотрение пользователя. Договоры в этой программе являются элементами справочника ДоговорыКонтрагентов, и их бизнес-реквизиты Номер и Дата по умолчанию заполняются вручную. Разберем подробно, как решить эту задачу с помощью доработки конфигурации или создания расширения.
В платформе «1С:Предприятие 8» встроенный механизм автоматического назначения номеров отлично работает для документов. Для справочников также существует автоматическая генерация, но она привязана к стандартному реквизиту Код. В справочнике ДоговорыКонтрагентов для хранения номера договора используется отдельный текстовый реквизит Номер (или НомерДоговора в некоторых версиях), а для даты — реквизит Дата (или ДатаДоговора соответственно). Стандартная платформа не умеет автоматически инкрементировать пользовательские текстовые поля без написания кода.
Среди начинающих разработчиков популярно мнение, что код генерации номера нужно помещать в процедуру ПриСозданииНаСервере формы элемента справочника. Проанализируем, почему этот подход ошибочен:
Правильное решение: Размещать алгоритм генерации номера и заполнения даты необходимо в предопределенной процедуре ПередЗаписью модуля объекта справочника ДоговорыКонтрагентов (или аналогичного метода в расширении конфигурации).
Если в вашей организации нет жестких требований к префиксам и сложной структуре номеров, можно использовать самое простое и элегантное решение — приравнять номер договора к автоинкрементному коду справочника, который платформа генерирует самостоятельно и гарантированно без дублей.
Рассмотрим пример кода, который необходимо добавить в модуль объекта справочника ДоговорыКонтрагентов в процедуру ПередЗаписью:
Процедура ПередЗаписью(Отказ, Замещение)
// Проверяем, что это новый элемент справочника
Если ЭтоНовый() Тогда
// Автоматически заполняем дату текущим числом, если она пустая
Если Не ЗначениеЗаполнено(Дата) Тогда
Дата = ТекущаяДата();
КонецЕсли;
// Устанавливаем признак необходимости генерации кода платформой
// После генерации кода присваиваем его в реквизит Номер
ЭтотОбъект.УстановитьНовыйКод();
ЭтотОбъект.Номер = СокрЛП(ЭтотОбъект.Код);
КонецЕсли;
КонецПроцедуры
При таком подходе платформа сама задействует стандартный механизм блокировок транзакций, выделит уникальный код для справочника и запишет его в поле номера. Это исключает возникновение дублей даже при высокой интенсивности работы пользователей.
Если вам требуется вести чистую нумерацию без буквенных префиксов справочника (например, просто по порядку: 1, 2, 3... 105), нам потребуется написать запрос к базе данных, получить максимальный текущий номер, увеличить его на единицу и присвоить новому договору.
Выясним причину, почему нельзя просто использовать функцию МАКСИМУМ(Номер) в запросе по текстовому полю. В базах данных текстовые строки сортируются посимвольно. Это означает, что строка "9" при сортировке будет считаться больше, чем строка "10" или "100". Чтобы обойти это ограничение, мы должны принудительно приводить номера к фиксированной длине с ведущими нулями (например, "00000001", "00000002") либо преобразовывать строку в число в коде.
Разберем по шагам безопасный алгоритм генерации числового номера в модуле объекта:
Процедура ПередЗаписью(Отказ, Замещение)
Если ЭтоНовый() Тогда
// 1. Установка даты по умолчанию
Если Не ЗначениеЗаполнено(Дата) Тогда
Дата = ТекущаяДата();
КонецЕсли;
// 2. Получение максимального номера из базы данных за текущий год
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Договоры.Номер КАК Номер
|ИЗ
| Справочник.ДоговорыКонтрагентов КАК Договоры
|ГДЕ
| Договоры.Дата МЕЖДУ &НачалоГода И &КонецГода
| И Договоры.ПометкаУдаления = ЛОЖЬ";
Запрос.УстановитьПараметр("НачалоГода", НачалоГода(Дата));
Запрос.УстановитьПараметр("КонецГода", КонецГода(Date));
РезультатЗапроса = Запрос.Выполнить();
МаксимальныйНомер = 0;
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Пытаемся преобразовать текстовый номер в число для точного сравнения
Попытка
ЧислоНомера = Число(СокрЛП(Выборка.Номер));
Если ЧислоНомера > МаксимальныйНомер Тогда
МаксимальныйНомер = ЧислоНомера;
КонецЕсли;
Исключение
// Если номер содержит буквы или символы, игнорируем его при поиске максимального числа
Продолжить;
КонецПопытки;
КонецЦикла;
КонецЕсли;
// 3. Увеличиваем номер на единицу
НовыйЧисловойНомер = МаксимальныйНомер + 1;
// 4. Форматируем номер с ведущими нулями для красивого отображения (например, "00001")
ЭтотОбъект.Номер = Формат(НовыйЧисловойНомер, "ЧЦ=5; ЧДЦ=0; ЧГ=0");
КонецЕсли;
КонецПроцедуры
Обратите внимание на то, что в данном запросе мы ограничили выборку рамками текущего года с помощью параметров НачалоГода и КонецГода. Это позволяет начать нумерацию заново с наступлением нового календарного года, что соответствует правилам ведения делопроизводства на большинстве предприятий.
Чтобы не изменять типовую конфигурацию «1С:Управление торговлей» и сохранить возможность автоматического обновления базы в будущем, рекомендуется реализовать данный функционал через расширение. Для этого выполним следующие действия:
ДоговорыКонтрагентов, нажмите по нему правой кнопкой мыши и выберите команду Добавить в расширение.ПередЗаписью с помощью аннотации &Перед.Посмотрим на пример кода, который пишется в модуле объекта внутри расширения:
&Перед("ПередЗаписью")
Процедура Расш1_ПередЗаписью(Отказ, Замещение)
// Проверяем, что объект создается впервые
Если ЭтотОбъект.ЭтоНовый() Тогда
// Заполнение даты
Если Не ЗначениеЗаполнено(ЭтотОбъект.Дата) Тогда
ЭтотОбъект.Дата = ТекущаяДата();
КонецЕсли;
// Поиск последнего номера и инкремент
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Договоры.Номер КАК Номер
|ИЗ
| Справочник.ДоговорыКонтрагентов КАК Договоры
|ГДЕ
| Договоры.Дата >= &НачалоГода";
Запрос.УстановитьПараметр("НачалоГода", НачалоГода(ЭтотОбъект.Дата));
Выборка = Запрос.Выполнить().Выбрать();
МаксНомер = 0;
Пока Выборка.Следующий() Цикл
Попытка
ТекНомер = Число(СокрЛП(Выборка.Номер));
Если ТекНомер > МаксНомер Тогда
МаксНомер = ТекНомер;
КонецЕсли;
Исключение
Продолжить;
КонецПопытки;
КонецЦикла;
ЭтотОбъект.Номер = Формат(МаксНомер + 1, "ЧГ=0");
КонецЕсли;
КонецПроцедуры
Использование расширений — наиболее современный и безопасный метод кастомизации продуктов 1С, гарантирующий стабильную работу системы после последующих обновлений со стороны вендора.
При внедрении автонумерации договоров обязательно учитывайте следующие архитектурные особенности платформы 1С: