В процессе разработки конфигураций на платформе 1С:Предприятие 8 часто возникает задача реализации индивидуальной нумерации документов для различных контрагентов или заказчиков. Например, при выборе заказчика в документе его номер должен автоматически принимать вид ПРЕФИКС/00001, где префикс жестко привязан к конкретному контрагенту. Попытки решить эту задачу «в лоб» через ручной перебор существующих номеров в базе данных приводят к серьезным ошибкам, зависаниям системы и нарушению уникальности номеров. В этой статье мы подробно проанализируем ошибки ручного подхода и разберем правильные, производительные методы решения этой задачи с использованием штатных механизмов платформы 1С.
Рассмотрим код, который часто пытаются использовать начинающие разработчики (или специалисты, перешедшие с устаревшей платформы 1С 7.7). Алгоритм пытается в цикле искать существующие документы и прибавлять единицу к номеру:
// Пример нерабочего и опасного кода
Префикс = "" + СокрЛП(Объект.Заказчик.КодНумерации) + "/";
Док = Документы.ЗаявкаНаПеревозку.СоздатьДокумент();
Ном = 1;
Пока 1 = 1 Цикл
Если Документы.ЗаявкаНаПеревозку.НайтиПоНомеру(Префикс + Ном, Док.Дата) = 1 Тогда
Если Объект.Ссылка = Док.Ссылка Тогда
Док.Номер = Префикс + Ном;
Прервать;
КонецЕсли;
Ном = Ном + 1;
Продолжить;
Иначе
Объект.Номер = Префикс + Ном;
Прервать;
КонецЕсли;
КонецЦикла;
Давайте подробно разберем, почему данный код является неработоспособным и опасным для производительности системы:
НайтиПоНомеру возвращает объект типа ДокументСсылка (если документ найден) или значение Неопределено (если документ не найден). Сравнение полученного результата с числом 1 (как в конструкции ... = 1 Тогда) всегда будет ложным. В итоге код всегда будет уходить в ветку Иначе и присваивать первому же документу номер с окончанием 1, игнорируя существующие документы в базе.Объект.Ссылка = Док.Ссылка не имеет смысла для новых документов. Метод СоздатьДокумент() создает в оперативной памяти новый объект, ссылка которого в базе данных еще не сформирована (она пустая). Ссылка у нового документа Объект, который еще не записан, также является пустой. Сравнение двух пустых ссылок вернет Истина, но логика работы программы при этом полностью нарушается.ЗАКАЗ/00025). При попытке записи документа первый пользователь успешно сохранит его, а второй получит критическую системную ошибку нарушения уникальности номера.НайтиПоНомеру выполняет полноценный SQL-запрос к базе данных. Если по данному заказчику в базе уже создано 10 000 документов, то при создании 10 001-го документа ваш цикл выполнит ровно 10 000 последовательных запросов к СУБД! Это приведет к моментальной перегрузке сервера и полному зависанию сеанса пользователя.Для решения этой задачи в платформе 1С:Предприятие 8 реализован мощный встроенный механизм автонумерации. Нам не нужно самостоятельно искать свободные номера в цикле — платформа умеет делать это автоматически и транзакционно, предотвращая конкурентные конфликты. Рассмотрим правильные способы реализации.
Самым надежным и архитектурно правильным способом является назначение префикса в модуле объекта документа. Это гарантирует непрерывность нумерации и корректную работу транзакций в многопользовательской среде. Процесс генерации номера происходит непосредственно в момент записи документа в базу данных.
Откроем Модуль объекта нашего документа (например, ЗаявкаНаПеревозку) и найдем предопределенную процедуру-обработчик ПриУстановкеНовогоНомера. Напишем в ней следующий код:
Процедура ПриУстановкеНовогоНомера(СтандартнаяОбработка, Префикс)
// Проверяем, заполнено ли поле Заказчик и его код нумерации
Если ЗначениеЗаполнено(Заказчик) И ЗначениеЗаполнено(Заказчик.КодНумерации) Тогда
// Формируем индивидуальный префикс для автонумератора платформы
Префикс = СокрЛП(Заказчик.КодНумерации) + "/";
// Оставляем СтандартнаяОбработка = Истина, чтобы платформа сама
// подобрала свободный номер на основе нашего нового префикса
КонецЕсли;
КонецПроцедуры
При таком подходе, когда пользователь нажимает кнопку «Записать» или «Провести», платформа перехватывает управление, видит измененный параметр Префикс, самостоятельно находит максимальный номер с этим префиксом в базе данных и инкрементирует его числовую часть (например, превращая ИВАНОВ/00012 в ИВАНОВ/00013) на уровне СУБД с наложением кратковременной блокировки.
Если бизнес-требования вашей задачи жестко диктуют, что пользователь должен видеть итоговый номер документа сразу же на форме (в момент выбора заказчика, до записи документа в базу), мы можем использовать метод объекта УстановитьНовыйНомер().
Для этого в форме документа для поля ввода Заказчик настроим событие ПриИзменении. Разберем по шагам реализацию этого механизма:
&НаКлиенте
Процедура ЗаказчикПриИзменении(Элемент)
// Передаем управление на сервер для работы с объектом документа
УстановитьНомерПоЗаказчикуНаСервере();
КонецПроцедуры
&НаСервере
Процедура УстановитьНомерПоЗаказчикуНаСервере()
// Получаем прикладной объект из реквизита формы "Объект"
ДокументОбъект = РеквизитФормыВЗначение("Объект");
Если ЗначениеЗаполнено(ДокументОбъект.Заказчик) И ЗначениеЗаполнено(ДокументОбъект.Заказчик.КодНумерации) Тогда
НовыйПрефикс = СокрЛП(ДокументОбъект.Заказчик.КодНумерации) + "/";
// Используем встроенный метод платформы вместо ручных циклов
ДокументОбъект.УстановитьНовыйНомер(НовыйПрефикс);
// Возвращаем измененный объект обратно в форму
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецЕсли;
КонецПроцедуры
Метод УстановитьНовыйНомер() мгновенно анализирует базу данных, генерирует следующий уникальный номер с переданным префиксом и записывает его в реквизит Номер нашего объекта. Пользователь сразу увидит актуальный номер на экране.
При работе с префиксацией и автонумерацией документов в 1С необходимо учитывать несколько критических факторов:
ТрансЛогистик — 13 символов), то префикс ТрансЛогистик/ физически не поместится в поле номера, либо сотрет его цифровую часть. Обязательно зайдите в свойства вашего документа в Конфигураторе и увеличьте длину номера с запасом (например, до 20-25 символов).ТЛ/001, а затем вы измените логику и префикс станет ТЛ/0001, платформа может начать сбиваться при определении следующего свободного номера.А/0000001). Не пытайтесь вручную убирать эти нули или писать сложные парсеры строк. Платформа делает это для сохранения правильной сортировки номеров на уровне СУБД. Нарушение этой структуры приведет к тому, что после номера 9 новым номером станет не 10, а снова 9 из-за особенностей текстовой сортировки.ПриУстановкеНовогоНомера может быть неэффективно, так как БСП затрет ваши изменения префиксами организации или узла распределенной базы данных (РИБ). В таких конфигурациях правильнее использовать общий модуль ПрефиксацияОбъектовСобытия и добавлять свои алгоритмы через подписки на события или механизмы интеграции БСП.Таким образом, мы выяснили причину неработоспособности ручного кода, проанализировали опасности его использования и внедрили два надежных решения на базе штатного автонумератора 1С. Использование обработчика ПриУстановкеНовогоНомера или метода УстановитьНовыйНомер() гарантирует быструю, стабильную и безопасную работу вашей базы данных даже при одновременной работе сотен пользователей.