Как сбить автонумерацию в 1С для тестирования метода ОбновитьНумерациюОбъектов

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

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

Два принципиально разных вида сбоя нумерации в 1С

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

  1. Визуальный (символьный) сбой. Этот сбой происходит на уровне текстового анализа номера платформой. Если пользователь вручную редактирует номер документа и вводит некорректный символ в конец строки (например, заменяет последнюю цифру буквой, добавляет косую черту или знак препинания вроде 0000-001/А), встроенный алгоритм генерации номеров 1С теряет ориентир. Платформа пытается распарсить строковый номер справа налево, чтобы найти числовую часть и инкрементировать ее. Не найдя число в конце строки, система не может прибавить единицу, из-за чего автонумерация "застревает" или начинает выдавать ошибки уникальности. Метод ОбновитьНумерациюОбъектов() в этом случае абсолютно бесполезен, так как некорректный номер физически записан в базе данных. Поможет только ручное или программное переименование ошибочного номера.
  2. Кэш-уровневый сбой (фантомные номера). При этом сбое формат всех номеров в базе данных остается идеальным (например, все номера строго числовые и последовательные: 00001, 00002, 00003), однако при создании нового документа платформа неожиданно выдает номер с огромным пропуском, например 00100. Это связано с тем, что сервер приложений 1С хранит кэш последнего выданного номера в оперативной памяти. Если максимальный документ с номером 00099 был физически удален из базы или перенесен в другой период, сервер "не замечает" этого в реальном времени и продолжает отсчет от ранее запомненного значения. Именно для устранения таких несоответствий и предназначен метод ОбновитьНумерациюОбъектов().

Как устроен кэш автонумерации на сервере приложений 1С

Для оптимизации производительности в многопользовательском режиме платформа 1С не обращается к СУБД с тяжелыми запросами вроде SELECT MAX(Номер) при каждом создании нового документа. Вместо этого сервер приложений 1С (процесс rphost) кэширует диапазоны номеров в оперативной памяти.

Поведение этого кэша напрямую регулируется свойством объекта метаданных "Режим автонумерации объектов". Рассмотрим два режима его работы:

Теперь, понимая внутреннюю кухню платформы, мы можем рассмотреть практические способы воспроизведения кэш-уровневого сбоя для тестирования.

Способ 1. Искусственная генерация и удаление объектов (Классический метод)

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

Разберем по шагам код обработки, выполняющей эту операцию:


// Создаем новый объект документа программно
НовыйДокумент = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
НовыйДокумент.Дата = ТекущаяДата();

// Запускаем цикл для искусственного увеличения счетчика автонумерации на сервере
Для Итератор = 1 По 5 Цикл
    НовыйДокумент.УстановитьНовыйНомер();
КонецЦикла;

// Записываем документ с завышенным номером в базу данных
НовыйДокумент.Записать();

// Получаем ссылку на только что созданный документ
СсылкаНаУдаление = НовыйДокумент.Ссылка;

// Удаляем документ непосредственно из базы данных в обход корзины
СсылкаНаУдаление.ПолучитьОбъект().Удалить();

Почему это сбивает нумерацию? При вызове метода УстановитьНовыйНомер() пять раз подряд сервер приложений фиксирует в своем кэше последнее выданное значение. Мы записываем этот объект в базу данных (тем самым легализуя номер в системе), а затем сразу удаляем его методом Удалить(). В результате в базе данных последним остается документ, например, с номером 00005, но серверный кэш уверен, что последний выданный номер — 00010. При создании следующего документа система выдаст номер 00011, пропустив номера с 6 по 10. Вызов метода ОбновитьНумерациюОбъектов() после этой процедуры должен вернуть нумерацию к значению 00006.

Способ 2. Изменение или откат максимального номера назад

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

Выполним следующие шаги для воспроизведения сбоя:

  1. Создадим последовательно 10 документов с номерами от 00001 до 00010.
  2. Найдем документ с номером 00010 и программно или вручную изменим его номер на 00005 (убедившись, что этот номер свободен в текущем периоде, либо предварительно переименовав старый документ).
  3. После записи изменений максимальный физический номер в таблице СУБД станет равен 00009.
  4. Попробуем интерактивно создать новый документ. Платформа 1С обратится к кэшу процесса rphost, который все еще "помнит" выданный ранее номер 00010, и автоматически присвоит новому документу номер 00011. Налицо необоснованный пропуск номеров в базе данных.

Для восстановления правильной последовательности нам потребуется очистить кэш сервера с помощью тестируемого метода.

Способ 3. Использование транзакций и режима «Не освобождать автоматически»

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

Для этого мы можем использовать следующий программный код:


НачатьТранзакцию();

Для Счетчик = 1 По 10 Цикл
    ТестовыйОбъект = Документы.ЗаказПокупателя.СоздатьДокумент();
    ТестовыйОбъект.Дата = ТекущаяДата();
    // Запрашиваем новый номер у сервера приложений
    ТестовыйОбъект.УстановитьНовыйНомер();
КонецЦикла;

// Отменяем транзакцию, не записывая созданные объекты в базу данных
ОтменитьТранзакцию();

Поскольку транзакция была отменена, ни один из десяти сгенерированных документов не запишется в базу данных СУБД. Однако из-за специфики режима "Не освобождать автоматически" сервер приложений 1С зафиксирует эти 10 номеров как использованные. При создании следующего документа в пользовательском режиме нумерация начнется сразу с одиннадцатой позиции, создав огромную пустоту в реестре документов.

Способ 4. Прямое удаление записей на уровне СУБД

Этот способ отлично моделирует последствия аварийного восстановления таблиц СУБД из резервной копии или некорректной работы сторонних интеграционных утилит в клиент-серверном варианте работы 1С.

Проанализируем алгоритм действий:

  1. В пользовательском режиме создадим цепочку документов до номера 00050. Сервер приложений 1С кэширует эту позицию как максимальную.
  2. Откроем консоль управления вашей СУБД (MS SQL Server Management Studio, pgAdmin и т.д.).
  3. Выполним прямой SQL-запрос на удаление последних пяти документов в обход платформы 1С. Например:
    
    DELETE FROM _Document150 WHERE _Number FIELD LIKE '%00045' OR _Number FIELD LIKE '%00050';
    
    *(Где _Document150 — имя физической таблицы документов в вашей базе данных).*
  4. Вернемся в клиент 1С и попробуем создать новый документ. Мы увидим, что платформа сгенерирует номер 00051, несмотря на то, что физически в базе данных последним остался документ с номером 00044.

Этот жесткий рассинхрон оперативной памяти сервера приложений и физических таблиц базы данных гарантированно лечится только вызовом метода обновления нумерации объектов.

Способ 5. Симуляция сбоя через распределенные базы данных (РИБ)

Рассмотрим специфический сценарий, характерный для территориально распределенных систем, использующих механизмы РИБ:

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

Как запустить и проверить работу метода ОбновитьНумерациюОбъектов

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

Обратите внимание, что метод ОбновитьНумерациюОбъектов() принимает на вход массив метаданных или конкретный объект метаданных. Разберем пример программного вызова для очистки кэша конкретного типа документов:


// Создаем массив с описанием типов объектов, для которых нужно сбросить кэш
МассивМетаданных = Новый Массив;
МассивМетаданных.Добавить(Метаданные.Документы.РеализацияТоваровУслуг);

// Вызываем метод платформы для обновления кэша автонумерации
ОбновитьНумерациюОбъектов(МассивМетаданных);

// Сообщаем пользователю об успешном завершении операции
Сообщить("Кэш автонумерации для документа 'Реализация товаров и услуг' успешно сброшен!");

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

← На главную