При интеграции систем или загрузке данных из внешних файлов разработчики нередко сталкиваются с ситуацией, когда стандартный поиск документа по номеру не дает результатов. Например, во входящем файле указан номер вида "44106", а в информационной базе документ записан со значением "44 106 ". Выясним причину такого поведения платформы, проанализируем последствия для нумератора и разберем практические способы решения задачи.
Рассмотрим, откуда в реквизите Номер появляются лишние символы:
"" + Число или вызов Строка(Число)), платформа 1С применяет региональные настройки форматирования по умолчанию. В них разделителем триад цифр является неразрывный пробел с кодом 160 (в коде 1С доступен через Символы.НПП). В результате число 44106 превращается в "44 106" с неразрывным пробелом между группами. Чтобы избежать такой ошибки при приведении числа к строке, следует всегда явно отключать группировку: Формат(Число, "ЧГ=0").Номер у документов имеет тип «Строка» фиксированной длины. Если в данный реквизит записывается строка меньшей длины, платформа автоматически дополняет её пробелами справа до максимального количества символов, определенного в метаданных.Проанализируем работу метода менеджера НайтиПоНомеру(). Данный метод выполняет точное бинарное сопоставление всей строки целиком. Если в базе номер дополнен концевыми пробелами или содержит скрытые символы разделителей групп, стандартный поиск по «чистому» номеру вернет пустую ссылку. Кроме того, если для документа настроена периодичность нумерации (например, в пределах года), метод обязательно требует передачи даты для определения соответствующего расчетного периода.
Посмотрим, к каким критическим последствиям приводит сохранение номеров с пробелами. Внутренний механизм автонумерации платформы выделяет числовую часть, анализируя символы с конца строки. Встретив пробел, автонумератор отсекает левую часть и считывает только правые цифры (в нашем примере — 106). Это приводит к тому, что последующие создаваемые документы получают заниженные номера либо вызывают фатальные ошибки контроля уникальности номеров в периоде.
Наиболее правильным и надежным подходом является очистка базы от некорректных номеров. Это не только упростит поиск при обмене, но и восстановит нормальное функционирование встроенного автонумератора 1С.
Разберем по шагам алгоритм пакетного исправления номеров документов:
Номер неразрывные и обычные пробелы с помощью функции СтрЗаменить().ОбновитьНумерациюОбъектов(), чтобы платформа сбросила закешированный счетчик и пересчитала следующий свободный номер.Посмотрим на пример кода обработки для исправления номеров:
Выборка = Документы.РеализацияТоваровУслуг.Выбрать(НачалоГода(ТекущаяДата()), КонецГода(ТекущаяДата()));
Пока Выборка.Следующий() Цикл
ТекущийНомер = Выборка.Номер;
// Удаляем неразрывные пробелы и стандартные пробелы
ОчищенныйНомер = СтрЗаменить(ТекущийНомер, Символы.НПП, "");
ОчищенныйНомер = СокрЛП(СтрЗаменить(ОчищенныйНомер, " ", ""));
Если ОчищенныйНомер <> СокрЛП(ТекущийНомер) Тогда
ДокументОбъект = Выборка.ПолучитьОбъект();
ДокументОбъект.Номер = ОчищенныйНомер;
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
КонецЕсли;
КонецЦикла;
// Восстанавливаем корректную работу автонумератора платформы
ОбновитьНумерациюОбъектов(Метаданные.Документы.РеализацияТоваровУслуг);
Если исправить данные в базе прямо сейчас невозможно (например, закрыт период для редактирования), рассмотрим вариант гибкого поиска средствами языка запросов. Поскольку функция ФОРМАТ() в запросах 1С отсутствует, а функция СТРЗАМЕНИТЬ() появилась только в платформе 8.3.20, универсальным решением является использование оператора ПОДОБНО с шаблонами подстановки.
Символ _ (подчеркивание) означает ровно один любой символ (включая неразрывный пробел), а символ % обозначает любую последовательность символов. Сформируем шаблон поиска на основе полученного из файла значения:
НомерИзФайла = "44106"; // Значение из внешнего источника
// Формируем шаблон с подстановочным знаком для возможного разделителя тысяч
Если СтрДлина(НомерИзФайла) = 5 Тогда
ШаблонПоиска = Лев(НомерИзФайла, 2) + "_" + Прав(НомерИзФайла, 3) + "%";
Иначе
ШаблонПоиска = "%" + НомерИзФайла + "%";
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка,
| Док.Номер КАК Номер
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Док
|ГДЕ
| Док.Дата МЕЖДУ &ДатаНачала И &ДатаКонца
| И Док.Номер ПОДОБНО &ШаблонПоиска";
Запрос.УстановитьПараметр("ДатаНачала", НачалоГода(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонца", КонецГода(ТекущаяДата()));
Запрос.УстановитьПараметр("ШаблонПоиска", ШаблонПоиска);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Дополнительно проверяем очищенный номер в коде для исключения ложных совпадений
НомерВБазеОчищенный = СтрЗаменить(СтрЗаменить(Выборка.Номер, Символы.НПП, ""), " ", "");
Если НомерВБазеОчищенный = НомерИзФайла Тогда
НайденныйДокумент = Выборка.Ссылка;
Прервать;
КонецЕсли;
КонецЦикла;
Рассмотрим вариант прямого сопоставления в запросе, если информационная база работает на версии платформы 8.3.20 или более новой. Начиная с этой версии, язык запросов поддерживает функцию СТРЗАМЕНИТЬ(), что позволяет избавиться от лишних символов непосредственно в условии ГДЕ.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Док
|ГДЕ
| Док.Дата МЕЖДУ &ДатаНачала И &ДатаКонца
| И СТРЗАМЕНИТЬ(СТРЗАМЕНИТЬ(Док.Номер, "" "", """"), &НПП, """") = &НомерИзФайла";
Запрос.УстановитьПараметр("ДатаНачала", НачалоГода(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонца", КонецГода(ТекущаяДата()));
Запрос.УстановитьПараметр("НомерИзФайла", "44106");
Запрос.УстановитьПараметр("НПП", Символы.НПП);
Результат = Запрос.Выполнить();
Если Не Результат.Пустой() Тогда
Выборка = Результат.Выбрать();
Выборка.Следующий();
НайденныйДокумент = Выборка.Ссылка;
КонецЕсли;
Подведем итог: использование временных обходных путей поиска через оператор ПОДОБНО или строковые функции в запросе оправдано лишь как срочная мера. Для стабильной работы конфигурации и корректного формирования номеров системой необходимо очистить реквизиты документов от пробелов и выполнить вызов метода ОбновитьНумерациюОбъектов().