Симптом
Бухгалтер создаёт счёт-фактуру на аванс, нажимает «Записать» и получает:
Значение "0000-А999" поля "Номер" не уникально
Дубля никто не вводил. Документ с таким номером в базе действительно есть, но он один, и программа сама, автоматически, пытается выдать новому документу тот же самый номер. Перезапуск и повторные попытки не помогают: ошибка повторяется на каждом новом счёте-фактуре этой серии.
Причина – в том, как платформа считает «следующий номер», и в одном-единственном номере, который когда-то ввели вручную короче остальных.
Как платформа подбирает следующий номер
Номер документа в типовых конфигурациях обычно строковый. У счёта-фактуры выданного в Бухгалтерии 3.0 это строка фиксированной длины в 12 символов, нумерация в пределах года. Номер состоит из префикса и числовой части:
- префикс – префикс организации и информационной базы плюс буква серии (для счетов-фактур на аванс при отдельной нумерации – «А»), например
0000-А; - числовая часть – всё остальное, при автонумерации она дополняется нулями до полной длины:
000001.
Когда документу нужен новый номер, платформа:
- берёт наибольший номер с тем же префиксом за тот же период нумерации (для счёта-фактуры – год);
- сравнивает номера как строки, а не как числа;
- прибавляет единицу к числовой части наибольшего номера и сохраняет её ширину.
Пока все номера одной длины, строковое сравнение совпадает с числовым и всё работает. Проблемы начинаются, когда в серии появляется номер другой длины.
Опыт в демо-базе
Чтобы не пересказывать механизм по памяти, я проверил его на копии демонстрационной базы «Бухгалтерия предприятия, редакция 3.0» (релиз 3.0.122, платформа 8.5.1). Документы создавались кодом в отдельном году, чтобы серия была чистой, и затем удалялись. Результаты:
| Шаг | Что сделали | Что выдала программа |
|---|---|---|
| 1 | Новые счета-фактуры на аванс | 0000-А000001, 0000-А000002 |
| 2 | Вручную записали полный номер 0000-А000800 |
следующий номер – 0000-А000801 |
| 3 | Вручную записали короткий номер 0000-А726 |
следующий номер – 0000-А727, а не 0000-А000801 |
| 4 | В серии есть 0000-А999 |
следующий номер – снова 0000-А999 |
| 5 | Попытка записать новый документ | «Значение "0000-А999" поля "Номер" не уникально» |
| 6 | Вручную добавили правильный 0000-А001000 |
следующий номер – всё равно 0000-А999 |
| 7 | Дополнили короткие номера нулями: 0000-А000726, 0000-А000999 |
следующий номер – 0000-А001000 |
Шаг 3 – главное. Как число 726 меньше 800, и «по-человечески» следующим должен быть 0000-А000801. Но платформа выдала 0000-А727: наибольшим она посчитала короткий номер, потому что как строка он больше.

Строковое сравнение номеров
Строки сравниваются посимвольно слева направо. У 0000-А000800 и 0000-А726 первые шесть символов совпадают, а на седьмом у одного 0, у другого 7. Символ 7 больше, значит, и вся строка 0000-А726 больше – длина и остальные символы уже не важны.
Шаг 6 по той же причине: 0000-А001000 как строка меньше 0000-А999 (на седьмом символе 0 против 9), поэтому ввести вручную «правильный большой» номер не помогает. Не помогает и ОбновитьНумерациюОбъектов: платформа честно пересчитывает нумерацию по данным и снова находит наибольшим 0000-А999.
Почему это проявляется не сразу
Самое неприятное – номер без нулей ничего не ломает в момент ввода. Наоборот, серия продолжает нумероваться «нормально», только от него: 0000-А727, 0000-А728 и так далее. Числовая часть у них трёхзначная, потому что платформа сохраняет ширину числовой части наибольшего номера.
На печати ничего не заметно. Бухгалтерия 3.0 печатает номер счёта-фактуры без префикса информационной базы и без ведущих нулей, поэтому и 0000-А000725, и 0000-А725 печатаются одинаково – А725. Пользователи видят ровную нумерацию, а в базе тем временем копятся сотни коротких номеров.
Развязка наступает на 999. Прибавить единицу в трёх разрядах нельзя, ширина не растёт, и платформа выдаёт тот же наибольший номер 0000-А999. Документ с ним уже есть, и запись прерывается ошибкой уникальности. С этого момента ни один новый счёт-фактура этой серии не записывается.

Как серия уходит в сторону
В рабочей базе, где я впервые разбирал эту ошибку, всё началось с одного документа: в номере случайно оказалось на один ноль меньше, пользователи стали поправлять номера вручную, и один номер ввели совсем без нулей. Дальше автонумерация честно выдала ещё 270 с лишним коротких номеров и остановилась на 999.
Ещё две особенности платформы, которые стоит знать
Номер, выданный несохранённому документу, пропадает. В опыте я запросил следующий номер, не записывая документ, и получил 0000-А726. Следующий записанный документ получил уже 0000-А727: платформа запоминает последний выданный номер, даже если документ так и не сохранили. Отсюда пропуски в нумерации, когда новый документ открыли и закрыли без записи.
В режиме загрузки уникальность номера не проверяется. Если записать документ с ОбменДанными.Загрузка = Истина, дубль номера записывается молча – в опыте так получились два документа с номером 0000-А999. Это важно для любой обработки, которая исправляет номера: проверять уникальность ей придётся самой.
Как найти проблемные номера
Достаточно выбрать номера серии за год и посмотреть на длину числовой части. Функция ниже возвращает короткие номера и показывает, какой номер платформа сейчас считает наибольшим:
// Номера серии счетов-фактур за год, у которых числовая часть короче полной.
// Префикс - например "0000-А", ДатаВГоду - любая дата нужного года.
Функция КороткиеНомераСерии(Префикс, ДатаВГоду) Экспорт
ДлинаЧисла = Метаданные.Документы.СчетФактураВыданный.ДлинаНомера - СтрДлина(Префикс);
Запрос = Новый Запрос(
"ВЫБРАТЬ
| СФ.Ссылка КАК Ссылка,
| СФ.Номер КАК Номер
|ИЗ
| Документ.СчетФактураВыданный КАК СФ
|ГДЕ
| СФ.Дата МЕЖДУ &Начало И &Конец
| И СФ.Номер ПОДОБНО &Шаблон");
Запрос.УстановитьПараметр("Начало", НачалоГода(ДатаВГоду));
Запрос.УстановитьПараметр("Конец", КонецГода(ДатаВГоду));
Запрос.УстановитьПараметр("Шаблон", Префикс + "%");
Короткие = Новый Массив;
Наибольший = "";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Номер = СокрП(Выборка.Номер);
Если Номер > Наибольший Тогда
Наибольший = Номер; // так же, строкой, сравнивает и платформа
КонецЕсли;
Если СтрДлина(Номер) - СтрДлина(Префикс) < ДлинаЧисла Тогда
Короткие.Добавить(Номер);
КонецЕсли;
КонецЦикла;
Сообщить("Наибольший номер серии: " + Наибольший + ", коротких номеров: " + Короткие.Количество());
Возврат Короткие;
КонецФункции
Если наибольший номер короче остальных, серия уже «уехала», даже если ошибки пока нет.
Как исправить
Короткие номера нужно дополнить нулями до полной длины: 0000-А725 → 0000-А000725. На печати номер не изменится – и до, и после исправления это А725, поэтому книга продаж и уже выданные контрагентам документы не затрагиваются.
Порядок действий:
- Сделать резервную копию базы.
- Для каждого короткого номера построить полный и убедиться, что такого номера за год ещё нет.
- Записать документы с новыми номерами. Без перепроведения: меняется только номер, движения остаются прежними.
- Попросить платформу пересчитать нумерацию по данным.
// Дополняет нулями короткие номера серии. Номера на печати не меняются.
Процедура ДополнитьНомераНулями(Префикс, ДатаВГоду) Экспорт
ДлинаЧисла = Метаданные.Документы.СчетФактураВыданный.ДлинаНомера - СтрДлина(Префикс);
Запрос = Новый Запрос(
"ВЫБРАТЬ
| СФ.Ссылка КАК Ссылка,
| СФ.Номер КАК Номер
|ИЗ
| Документ.СчетФактураВыданный КАК СФ
|ГДЕ
| СФ.Дата МЕЖДУ &Начало И &Конец");
Запрос.УстановитьПараметр("Начало", НачалоГода(ДатаВГоду));
Запрос.УстановитьПараметр("Конец", КонецГода(ДатаВГоду));
Номера = Запрос.Выполнить().Выгрузить();
// Номер уникален в пределах года среди всех счетов-фактур, не только в серии.
Занятые = Новый Соответствие;
Для Каждого Строка Из Номера Цикл
Занятые.Вставить(СокрП(Строка.Номер), Строка.Ссылка);
КонецЦикла;
НачатьТранзакцию();
Попытка
Для Каждого Строка Из Номера Цикл
Номер = СокрП(Строка.Номер);
Если Не СтрНачинаетсяС(Номер, Префикс) Тогда
Продолжить;
КонецЕсли;
Числовая = Сред(Номер, СтрДлина(Префикс) + 1);
Если СтрДлина(Числовая) >= ДлинаЧисла Тогда
Продолжить;
КонецЕсли;
НовыйНомер = Префикс + СтроковыеФункцииКлиентСервер.ДополнитьСтроку(Числовая, ДлинаЧисла);
Если Занятые.Получить(НовыйНомер) <> Неопределено Тогда
ВызватьИсключение "Номер " + НовыйНомер + " уже занят, исправьте его вручную";
КонецЕсли;
Объект = Строка.Ссылка.ПолучитьОбъект();
Объект.Номер = НовыйНомер;
// Режим загрузки: без проверок и перепроведения. Уникальность в этом режиме
// платформа не проверяет - поэтому она проверена выше.
Объект.ОбменДанными.Загрузка = Истина;
Объект.Записать(РежимЗаписиДокумента.Запись);
Занятые.Вставить(НовыйНомер, Строка.Ссылка);
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
ОбновитьНумерациюОбъектов(Метаданные.Документы.СчетФактураВыданный);
КонецПроцедуры
В опыте следующий номер стал правильным (0000-А001000) уже после записи исправленных номеров, и вызов ОбновитьНумерациюОбъектов ничего не поменял. В рабочей базе я его всё равно делаю: этот метод для того и предназначен, чтобы платформа пересчитала нумерацию по данным, и он ничего не ломает.
Проведённый документ, записанный в режиме загрузки, остаётся проведённым с прежними движениями. Провести документ в этом режиме нельзя – платформа запретит, – но здесь это и не нужно.
Если в серии есть «выброс»
Бывает и обратная ситуация: номер полной длины, но с лишним разрядом или опечаткой, например 0000-А002611 среди номеров до тысячи. Он тоже становится наибольшим, и платформа продолжит серию с 0000-А002612 – ошибки не будет, будет скачок нумерации.
Дополнение нулями здесь не поможет: чтобы вернуть нумерацию, придётся поменять номер самого документа, а вместе с ним изменится и номер на печати. Если счёт-фактура уже попал в книгу продаж или ушёл контрагенту, решение остаётся за бухгалтером. Свободный номер стоит подбирать той же даты, чтобы не нарушить хронологию.
Готовая обработка
К статье приложена обработка «Исправление нумерации счетов-фактур на аванс» – та самая, которой я исправлял серию в рабочей базе. Всё, что описано выше, она делает в одной форме:
- находит в серии за год номера без ведущих нулей и номера, которые не соответствуют шаблону серии;
- показывает, какой номер программа выдаст сейчас и какой – после исправления;
- находит выбросы и подбирает свободный номер той же даты, чтобы не нарушить хронологию;
- перед записью проверяет, что новый номер не занят среди всех счетов-фактур года;
- меняет только номер – без перепроведения, в одной транзакции, каждое изменение пишет в журнал регистрации;
- номера на печати не меняет.
Достаточно выбрать организацию и год и нажать «Заполнить». Префикс серии обработка подставляет сама, по тем же правилам, по которым Бухгалтерия нумерует новые счета-фактуры; при необходимости его можно поправить вручную.

Форма после заполнения: четыре коротких номера и выброс
Строки к исправлению отмечаются автоматически, с любой можно снять отметку; двойной щелчок по строке открывает счёт-фактуру. Кнопка «Исправить нумерацию» дополняет отмеченные номера нулями и сразу заполняет форму заново.

После исправления: коротких номеров нет, выброс оставлен на решение бухгалтера
Выброс обработка сама не меняет: у него изменится номер на печати, и решать это бухгалтеру. В подсказке уже указан свободный номер той же даты – его достаточно ввести в поле «Номер» полностью, с нулями.
Проверено на «Бухгалтерии предприятия» 3.0.122 (демо-база) и «Бухгалтерии предприятия КОРП» 3.0.202 (рабочая база). Обработка собрана на платформе 8.3.27, проверена на 8.5.1, код открыт. Её можно открыть через «Файл → Открыть» или подключить в «Дополнительных отчетах и обработках» как дополнительную обработку (безопасный режим нужно выключить: обработка записывает документы и обновляет нумерацию). Перед исправлением сделайте резервную копию базы.
Как не допустить
Короткий номер почти всегда появляется из-за ручной правки. Варианты защиты:
- не давать пользователям редактировать номер счёта-фактуры;
- проверять длину номера перед записью – например, подпиской на событие «Перед записью» в расширении:
// Обработчик подписки на событие ПередЗаписью документа СчетФактураВыданный.
Процедура ПроверитьДлинуНомераПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
Если Источник.ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
Номер = СокрП(Источник.Номер);
ДлинаНомера = Источник.Метаданные().ДлинаНомера;
Если ЗначениеЗаполнено(Номер) И СтрДлина(Номер) < ДлинаНомера Тогда
Отказ = Истина;
Сообщить("Номер " + Номер + " короче " + ДлинаНомера
+ " знаков. Дополните числовую часть нулями: так автонумерация не собьётся.");
КонецЕсли;
КонецПроцедуры
Пустой номер такая проверка пропускает: его присвоит сама платформа.
Итоги
- Следующий номер платформа считает от наибольшего номера серии как строки и сохраняет ширину его числовой части.
- Один номер без ведущих нулей становится «наибольшим», серия продолжается от него короткими номерами и упирается в
999с ошибкой «Значение поля "Номер" не уникально». - Ввести вручную правильный большой номер не поможет:
0000-А001000как строка меньше0000-А999. - На печати короткие номера не видны: Бухгалтерия печатает номер счёта-фактуры без ведущих нулей.
- Лечение – дополнить короткие номера нулями, проверив уникальность, и обновить нумерацию. Номера на печати при этом не меняются.
- Номера, выданные несохранённым документам, пропадают; в режиме загрузки дубли номеров не ловятся.
Если закрываете месяц в ERP, КА или УТ и расходятся остатки склада, организаций и себестоимости, загляните в мою обработку «Сверка остатков товаров: склад, организация, себестоимость – с черновиками исправлений».
Проверено на следующих конфигурациях и релизах:
- Бухгалтерия предприятия, редакция 3.0, релизы 3.0.122.97
- Бухгалтерия предприятия КОРП, редакция 3.0, релизы 3.0.202.14
Вступайте в нашу телеграмм-группу Инфостарт