Рано или поздно любой администратор или разработчик 1С сталкивается с ситуацией, когда пользователи начинают жаловаться на ошибку «Номер не уникален» при создании новых документов. В типовых конфигурациях автонумерация работает исправно до тех пор, пока в процесс не вмешивается человеческий фактор. Достаточно одному сотруднику вручную отредактировать номер, удалив лидирующие нули или случайно изменив префикс, как весь отлаженный механизм автоматического присвоения номеров выходит из строя.
В этой статье мы подробно разберем, почему ломается автонумерация в 1С:Бухгалтерия 2.0 (и других конфигурациях на обычных формах), как безопасно исправить ситуацию при наличии распределенных информационных баз (РИБ) и кастомных префиксов подразделений, а также разработаем оптимальный инструмент для поиска проблемных документов и их автоматической перенумерации.
Для того чтобы эффективно решить проблему, нам необходимо проанализировать ее техническую суть. Платформа 1С:Предприятие генерирует новые номера на основе лексикографического (алфавитно-цифрового) сравнения строк. Система ищет в базе данных документ с максимальным номером за текущий период (обычно за год) и увеличивает его числовую часть на единицу.
Логика работы ломается в следующих случаях:
00000000001). Если пользователь вручную сохраняет документ с номером 1 или 99, платформа при поиске максимального номера сравнивает первый символ строки. Символ «9» лексикографически больше, чем символ «0». Соответственно, короткий номер 9 система посчитает максимальным и попытается создать следующий документ с номером 10. Как только нумерация дойдет до 99, система упрется в предел разрядности и начнет выдавать ошибку уникальности.Ситуация значительно усложняется, если пользователи работают в распределенной информационной базе (РИБ), а в системе реализован нетиповой механизм префиксации (например, подстановка префикса подразделения из настроек пользователя вместо префикса узла РИБ). В этом случае использование стандартных групповых обработок «в лоб» несет огромные риски:
Поэтому перед началом любых работ по исправлению номеров необходимо полностью приостановить обмены данными в РИБ до завершения процесса перенумерации во всех базах!
Если сбой произошел в типовой конфигурации и объем документов относительно небольшой, мы можем обойтись без написания кода, используя стандартный функционал:
Отчеты — Анализ учета — Экспресс-проверка. Данный отчет позволяет быстро выявить нарушения хронологии и уникальности номеров в кассовых документах (ПКО и РКО) и счетах-фактурах. Прямо из отчета можно запустить штатную обработку перенумерации.Номер, оставив поле пустым. Платформа автоматически сгенерирует новые корректные последовательные номера по порядку дат, начиная с первого свободного.Если в вашей базе используются сложные кастомные префиксы (как у автора темы), стандартные инструменты могут навредить. В таком случае напишем собственную обработку.
Рассмотрим, как оптимизировать алгоритм анализа документов, предложенный на форуме. Вместо непроизводительной выборки объектов через метод Выбрать() в цикле (который сильно нагружает базу данных), мы применим эффективный запрос к СУБД для группировки и поиска аномалий в длине номеров.
Проанализируем следующий код для анализа базы данных:
Процедура ПроанализироватьДлинуНомеров(ИмяДокумента, ВыбГод)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Ссылка КАК Ссылка,
| Номер КАК Номер
|ИЗ
| Документ." + ИмяДокумента + " КАК Док
|ГДЕ
| Дата МЕЖДУ &НачалоПериода И &КонецПериода";
Запрос.УстановитьПараметр("НачалоПериода", НачалоГода(Дата(ВыбГод, 1, 1)));
Запрос.УстановитьПараметр("КонецПериода", КонецГода(Дата(ВыбГод, 12, 31)));
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
Сообщить("Документов типа " + ИмяДокумента + " за " + ВыбГод + " год не найдено.");
Return;
КонецЕсли;
Выборка = РезультатЗапроса.Выбрать();
ОбщееКоличество = Выборка.Количество();
// Используем Соответствие для подсчета количества документов каждой длины
СтатистикаДлин = Новый Соответствие;
Пока Выборка.Следующий() Цикл
ДлинаТекущегоНомера = СтрДлина(СокрЛП(Выборка.Номер));
КоличествоДляДлины = СтатистикаДлин.Получить(ДлинаТекущегоНомера);
Если КоличествоДляДлины = Неопределено Тогда
СтатистикаДлин.Вставить(ДлинаТекущегоНомера, 1);
Иначе
СтатистикаДлин.Вставить(ДлинаТекущегоНомера, КоличествоДляДлины + 1);
КонецЕсли;
КонецЦикла;
Сообщить("--- Анализ документов """ + ИмяДокумента + """ за " + ВыбГод + " год ---");
Сообщить("Всего найдено документов: " + ОбщееКоличество);
Для Каждого СтрокаСтатистики Из СтатистикаДлин Цикл
Сообщить("Документов с длиной номера " + СтрокаСтатистики.Ключ + " симв.: " + СтрокаСтатистики.Значение);
КонецЦикла;
КонецПроцедуры
После того как мы локализовали проблему и определили, какие именно документы имеют некорректную длину номера, мы можем запустить процедуру программного исправления. Чтобы сохранить логику нетиповых префиксов подразделений, мы будем сбрасывать номера документов и вызывать стандартный метод УстановитьНовыйНомер(), который обратится к кастомному алгоритму в модуле объекта.
Рассмотрим по шагам программный алгоритм перенумерации в транзакции:
Процедура ВыполнитьПеренумерацию(ИмяДокумента, НачалоПериода, КонецПериода)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Ссылка КАК Ссылка
|ИЗ
| Документ." + ИмяДокумента + " КАК Док
|ГДЕ
| Дата МЕЖДУ &НачалоПериода И &КонецПериода
|УПОРЯДОЧИТЬ ПО
| Дата Возр";
Запрос.УстановитьПараметр("НачалоПериода", НачалоПериода);
Запрос.УстановитьПараметр("КонецПериода", КонецПериода);
Выборка = Запрос.Выполнить().Выбрать();
НачатьТранзакцию();
Попытка
Пока Выборка.Следующий() Цикл
ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
// Сбрасываем некорректный номер
ДокОбъект.Номер = "";
// Метод УстановитьНовыйНомер() автоматически сформирует правильный префикс
// организации, узла РИБ или подразделения (если это прописано в модуле)
ДокОбъект.УстановитьНовыйНомер();
ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
КонецЦикла;
ЗафиксироватьТранзакцию();
Сообщить("Перенумерация успешно завершена!");
Исключение
ОтменитьТранзакцию();
ВызватьИсключение "Ошибка перенумерации: " + ОписаниеОшибки();
КонецПопытки;
// КРИТИЧЕСКИ ВАЖНЫЙ ШАГ: Сброс кэша нумерации на сервере 1С!
ОбновитьНумерациюОбъектов(Метаданные.Документы[ИмяДокумента]);
КонецПроцедуры
Обратите внимание на вызов метода ОбновитьНумерациюОбъектов() в конце процедуры. Платформа 1С кэширует последние выданные номера в оперативной памяти сервера для повышения производительности. Если выполнить перенумерацию документов в базе данных, но не очистить этот кэш, сервер продолжит выдавать новые номера на основе старых «кривых» значений из своей памяти. Метод ОбновитьНумерациюОбъектов() принудительно сбрасывает этот кэш и заставляет систему рассчитать актуальный максимальный номер заново.
Чтобы вам больше не приходилось вручную или программно восстанавливать последовательность номеров, необходимо устранить первопричину — закрыть пользователям физическую возможность редактирования номеров.
Для этого мы можем применить один из следующих подходов:
Права, выберем роль обычного пользователя и снимем флажок Редактирование для реквизита Номер. Автоматическое заполнение номера при этом продолжит работать, но поле ввода станет недоступным для ручных изменений.В процедуре ПриОткрытии() формы документа добавим код:
Если НЕ РольДоступна("ПолныеПрава") Тогда
ЭлементыФормы.Номер.Доступность = Ложь;
КонецЕсли;
Используя приведенные программные методы и настроив права доступа, мы сможем быстро и безболезненно восстановить порядок в учете и гарантировать, что автонумерация больше никогда не собьется.