При внедрении или реорганизации систем учета перед разработчиками часто возникает задача изменения структуры кодов справочника Номенклатура. Типичный сценарий: заказчик требует отказаться от префиксов (например, превратить код "00-000007" в простой 6-значный "000007") и настроить систему так, чтобы следующий код автоматически генерировался как "000008".
Однако при попытке изменить существующие коды стандартными обработками программисты часто сталкиваются с проблемой: новые элементы справочника упорно продолжают получать старые префиксальные номера или дублируют уже существующие коды, игнорируя новую последовательность. В рамках этой статьи мы подробно разберем, почему так происходит, как устроен механизм автонумерации платформы «1С:Предприятие», и как правильно решить эту задачу без нарушения логики работы типовой конфигурации.
Выясним причину, по которой простое переименование кодов в базе данных не приводит к моментальному обновлению механизма автонумерации. Начиная с версии платформы 8.1, «1С:Предприятие» использует специальный Менеджер автонумерации. Для повышения производительности при одновременной работе множества пользователей платформа кэширует в оперативной памяти сервера последний выданный номер для каждого вида объектов метаданных и каждого префикса.
Если мы изменим коды существующих элементов напрямую (через групповое изменение реквизитов, обработкой или запросом), кэш в оперативной памяти сервера не обновится. Платформа продолжит считать, что последний выданный номер соответствует старому значению из кэша, и попытается записать объект с неуникальным кодом.
Чтобы принудительно очистить кэш сервера и заставить платформу обратиться напрямую к таблицам СУБД для поиска максимального существующего кода, мы должны использовать метод ОбновитьНумерациюОбъектов(). Напишем пример вызова этого метода:
// Метод должен выполняться на стороне сервера под учетной записью с административными правами
Процедура ОбновитьНумерациюНоменклатурыНаСервере() Экспорт
МетаданныеОбъекта = Метаданные.Справочники.Номенклатура;
ОбновитьНумерациюОбъектов(МетаданныеОбъекта);
КонецПроцедуры
После выполнения этой команды кэш сбросится, и менеджер автонумерации определит новый максимальный код на основе реальных данных из базы данных.
Рассмотрим подробнее, как именно платформа определяет «максимальный» код в базе данных. Код справочника является строковым реквизитом. Сравнение строковых значений в базах данных происходит по правилам лексикографического (алфавитного) порядка, а не математического сравнения чисел.
Проанализируем простую ситуацию. Если в справочнике останется хотя бы один некорректный код, например короткий код "9", то по правилам алфавитного сравнения строка "9" будет считаться больше, чем строка "000008" (так как символ "9" идет позже символа "0"). Обнаружив такой код, автонумератор платформы попытается создать следующий код на основе девятки и выдаст ошибку уникальности или сбой формата.
Именно поэтому перед запуском новой системы нумерации крайне важно привести абсолютно все существующие коды к строго фиксированной длине с ведущими нулями (например, ровно 6 знаков: "000001", "000002", ... "000123"). В кодах не должно оставаться пробелов, букв, старых дефисов или символов разной длины.
В современных типовых конфигурациях (таких как ERP, УТ 11, КА, БП) за генерацию кодов и номеров документов отвечает встроенная подсистема «Префиксация объектов» из состава Библиотеки Стандартных Подсистем (БСП). Даже если мы очистим префикс при записи в своем коде, стандартные обработчики БСП могут повторно перехватить событие и принудительно добавить префикс базы или организации.
Разберем по шагам, как правильно отключить генерацию префиксов без изменения кода конфигурации:
Номенклатура, лучшим решением будет использование расширения конфигурации. Нам потребуется переопределить логику в общем модуле ПрефиксацияОбъектовСобытия или перехватить подписку на событие ПриУстановкеНовогоКодаНоменклатуры, чтобы принудительно обнулять префикс для данного справочника.Если мы работаем в конфигурациях ERP, Комплексная автоматизация или Управление торговлей 11, то столкнемся со специфической архитектурой справочника Номенклатура. В этих конфигурациях у справочника есть дополнительный индексированный реквизит КодДляПоиска.
Этот реквизит предназначен для оптимизации работы полнотекстового поиска, динамических списков выбора и форм подбора товаров. Если мы программно изменим стандартный реквизит Код, но забудем про реквизит КодДляПоиска, пользователи не смогут находить товары по новым 6-значным номерам через стандартный поиск или в документах продажи/закупки.
При написании обработки перенумерации необходимо обязательно синхронизировать оба этих реквизита. Посмотрим на пример кода групповой обработки, которая решает эту задачу комплексно:
Выборка = Справочники.Номенклатура.Выбрать();
Пока Выборка.Следующий() Цикл
ОбъектНоменклатуры = Выборка.ПолучитьОбъект();
СтарыйКод = ОбъектНоменклатуры.Код;
// Очищаем код от старых префиксов (например, "00-")
ЧистыйКод = СтрЗаменить(СтарыйКод, "00-", "");
ЧистыйКод = СокрЛП(ЧистыйКод);
// Приводим код к строго 6-значному формату с ведущими нулями
ЦелеваяДлина = 6;
НовыйКод = ЧистыйКод;
Пока СтрДлина(НовыйКод) < ЦелеваяДлина Цикл
НовыйКод = "0" + НовыйКод;
КонецЦикла;
// Проверяем, изменился ли код, чтобы избежать лишней перезаписи объектов
Если ОбъектНоменклатуры.Код <> НовыйКод Тогда
ОбъектНоменклатуры.Код = НовыйКод;
// Обязательно проверяем наличие реквизита КодДляПоиска и обновляем его
Если ОбщегоНазначения.ЕстьРеквизитОбъекта("КодДляПоиска", Метаданные.Справочники.Номенклатура) Тогда
ОбъектНоменклатуры.КодДляПоиска = НовыйКод;
КонецЕсли;
Попытка
// Блокируем вызов некоторых тяжелых транзакционных обработчиков, если это допустимо
ОбъектНоменклатуры.ОбменДанными.Загрузка = Истина;
ОбъектНоменклатуры.Записать();
Исключение
Сообщить("Не удалось записать объект: " + ОбъектНоменклатуры.Ссылка + " Ошибка: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецЦикла;
// После успешного обновления всех кодов сбрасываем кэш автонумератора
ОбновитьНумерациюОбъектов(Метаданные.Справочники.Номенклатура);
В типовых конфигурациях 1С стандартная длина реквизита Код для номенклатуры обычно установлена с запасом — 11 символов. Возникает соблазн зайти в конфигуратор и уменьшить длину кода до 6 символов. Проанализируем, почему делать это настоятельно не рекомендуется:
Правильный подход: Оставьте длину кода в метаданных равной стандартным 11 символам. При этом программно записывайте в этот реквизит только 6 значащих цифр с ведущими нулями (например, "000123"). Оставшаяся часть строкового поля заполнится пробелами автоматически или останется пустой (в зависимости от настроек СУБД), что никак не повлияет на автонумерацию, если все коды в базе будут приведены к единому стандарту длины.
Таким образом, для успешного перехода на 6-значные коды без префиксов достаточно выполнить три последовательных шага: нормализовать существующие коды в базе данных (включая КодДляПоиска), отключить префиксы в настройках организаций и синхронизации, а затем выполнить метод ОбновитьНумерациюОбъектов() на сервере.