При выполнении задач по разделению учета, переносу данных или реорганизации структуры предприятия часто возникает необходимость изменить префиксы в номерах уже существующих документов. Однако при разработке простой внешней обработки разработчики нередко сталкиваются с ситуацией, когда вместо ожидаемого номера (например, БУХ000001) получается строка вида БУХБУХБУХ1 или нарушается сквозная автонумерация. Разберем по шагам, почему возникают такие ошибки и как правильно реализовать замену префикса.
Проанализируем типовой ошибочный код, который часто используют для модификации номера:
НовыйНомерДока = СтрЗаменить(Док.Номер, Лев(Док.Номер, 3), НовыйПрефикс);
Функция СтрЗаменить производит поиск и замену всех вхождений переданного образца во всей строке, а не только в ее начале. Если у документа был номер 000000001, выражение Лев(Док.Номер, 3) возвращает подстроку "000". Поскольку эта подстрока встречается в номере три раза подряд (000-000-000-1), функция заменяет каждое такое вхождение на новый префикс. В итоге номер превращается в БУХБУХБУХ1.
Кроме того, при обходе таблицы значений циклом Для каждого Стр Из Выборка Цикл нельзя удалять строки методом Выборка.Удалить(Стр) прямо внутри итератора — это приводит к сдвигу индексов коллекции и пропуску строк.
Если префиксы имеют фиксированную длину или вам требуется заменить первые N символов номера без изменения числовой части, используем функцию Сред, которая вырезает остаток строки начиная с нужной позиции:
ДлинаПрефикса = СтрДлина(НовыйПрефикс);
НовыйНомерДока = НовыйПрефикс + Сред(Док.Номер, ДлинаПрефикса + 1);
Если старый префикс и новый имеют разную длину, необходимо строго контролировать общую длину результирующего номера, чтобы она совпадала со свойством ДлинаНомера в объекте метаданных документа. Если результирующая длина строки изменится, нарушится строковая сортировка номеров в списках и механизм автонумерации платформы.
Платформа 1С предоставляет встроенный метод УстановитьНовыйНомер(<ПрефиксНомера>). Однако при его прямом вызове на существующих документах следует учитывать важные особенности:
ПриУстановкеНовогоНомераДокумента). Стандартные модули префиксации автоматически конкатенируют префикс узла информационной базы, префикс организации и переданный пользовательский префикс, что может исказить итоговый номер (например, получится БУХККК001 вместо ККК000001).Для корректного и безопасного обновления номеров документов без срабатывания тяжелых проверок бизнес-логики и подписок на события рекомендуется использовать установку флага ОбменДанными.Загрузка = Истина. Рассмотрим готовый пример процедуры:
Процедура ЗаменитьПрефиксыВДокументах(СтарыйПрефикс, НовыйПрефикс)
ДлинаПрефикса = СтрДлина(СтарыйПрефикс);
Для Каждого МетаДокумент Из Метаданные.Документы Цикл
// Проверяем, есть ли у документа номер и строковый ли он
Если МетаДокумент.ДлинаНомера = 0 Или МетаДокумент.ТипНомера <> Метаданные.СвойстваОбъектов.ТипНомераДокумента.Строка Тогда
Продолжить;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка,
| Док.Номер КАК Номер
|ИЗ
| Документ." + МетаДокумент.Имя + " КАК Док
|ГДЕ
| Док.Номер ПОДОБНО &ШаблонПрефикса";
Запрос.УстановитьПараметр("ШаблонПрефикса", СтарыйПрефикс + "%");
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
// Формируем новый номер с сохранением числовой части
ЧисловаяЧасть = Сред(ДокОбъект.Номер, ДлинаПрефикса + 1);
ДокОбъект.Номер = НовыйПрефикс + ЧисловаяЧасть;
// Отключаем обработчики бизнес-логики при записи
ДокОбъект.ОбменДанными.Загрузка = Истина;
Попытка
// Сохраняем текущий режим записи (проведен/не проведен)
Если ДокОбъект.Проведен Тогда
ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
Иначе
ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
КонецЕсли;
Исключение
Сообщить("Ошибка записи документа " + Строка(Выборка.Ссылка) + ": " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
// Сбрасываем кэш счетчиков нумерации для текущего вида документа
ОбновитьНумерациюОбъектов(МетаДокумент);
КонецЦикла;
Сообщить("Замена префиксов успешно завершена!");
КонецПроцедуры
После прямого программного изменения поля Номер внутренний механизм платформы может продолжать выдавать старые или задвоенные номера при создании новых документов. Чтобы сообщить платформе о необходимости заново определить максимальный занятый номер в базе, вызываем процедуру глобального контекста ОбновитьНумерациюОбъектов(). В качестве параметра в нее передается объект метаданных измененного документа (или массив объектов метаданных).