При разработке механизмов импорта данных, например, при разборе выписок из «Клиент-Банка» или обработке назначений платежей в платежных поручениях, программисты часто сталкиваются со странным поведением метода НайтиПоНомеру(). В этой статье мы подробно разберем ситуацию, когда поиск документа по строковому номеру и дате выдает «0» (не найдено), если значения передаются через переменные, но при этом отлично работает, если те же самые значения подставить в код вручную в виде текстовых литералов. Проанализируем причины этого явления и найдем надежное решение для платформы 1С:Предприятие 7.7.
Рассмотрим типичную ситуацию. Программист пытается найти документ, используя данные, извлеченные из текстовой строки. В отладчике («Табло») ситуация выглядит следующим образом:
ИщемНомер = "ТИ003965"
ИщемДату = '24.09.21'
// Попытка поиска через объект:
результат1 = объект.НайтиПоНомеру(ИщемНомер, ИщемДату); // Возвращает 0
// Попытка поиска прямой подстановкой значений:
результат2 = объект.НайтиПоНомеру("ТИ003965", '24.09.21'); // Возвращает 1
На первый взгляд это кажется нелогичным: значения в переменных визуально идентичны тем, что подставляются вручную. Однако корень проблемы кроется в том, как платформа 1С 7.7 обрабатывает типы данных и конкретно — формат года в датах.
Выясним причину такого поведения, изучив механизм формирования даты. В 1С 7.7 существует два принципиально разных способа задания даты в коде:
'24.09.21' (в одинарных кавычках). При компиляции этого выражения платформа обращается к системным настройкам (так называемое «Окно года»). Если в настройках указано, что 21 — это 2021 год, платформа автоматически подставит правильный век.Дата(Год, Месяц, День), платформа ведет себя иначе. Если передать в параметр Год число 21, система воспримет это буквально. В итоге будет создана дата 24 сентября 0021 года нашей эры.В этом и заключается основная ловушка. Когда мы пишем в табло отладки литерал, мы получаем дату 2021 года. Но когда код разбирает строку «24.09.21», выделяет оттуда число 21 и передает его в Дата(), поиск начинает искать документ в начале первого тысячелетия. Естественно, метод НайтиПоНомеру() возвращает 0, так как в 21-м году документов в вашей базе не существует.
Рассмотрим фрагмент кода, в котором происходит разбор строки назначения платежа для поиска счета или накладной. Проанализируем, где именно закрадывается ошибка формирования даты.
// Извлекаем дату из строки "сч.№ТИ003965 от 24.09.21"
Позиция_от_ = Найти(Назначение, " от ");
Если Позиция_от_ <> 0 Тогда
СтрокаДаты = Сред(Назначение, Позиция_от_ + 4, 8); // Получаем "24.09.21"
ПозицияТочки = Найти(СтрокаДаты, ".");
День = Лев(СтрокаДаты, ПозицияТочки - 1);
Остаток = Сред(СтрокаДаты, ПозицияТочки + 1);
ПозицияТочки2 = Найти(Остаток, ".");
Месяц = Лев(Остаток, ПозицияТочки2 - 1);
Год = Сред(Остаток, ПозицияТочки2 + 1);
// ОШИБКА: Если Год = 21, то Дата() вернет 0021 год
ИщемДату = Дата(Число(Год), Число(Месяц), Число(День));
КонецЕсли;
объект = СоздатьОбъект("Документ.Счет");
Если объект.НайтиПоНомеру(ИщемНомер, ИщемДату) = 1 Тогда
Сообщить("Документ найден!");
КонецЕсли;
В данном примере, если в строке год указан двумя цифрами, переменная ИщемДату примет значение, далекое от реальности. При этом визуально в Сообщить() это может выглядеть как "24.09.21", и программист не сразу заметит подвох.
Чтобы метод НайтиПоНомеру() работал корректно, необходимо гарантировать, что год передается в функцию Дата() в четырехзначном формате. Рассмотрим наиболее надежный способ преобразования двухзначного года в четырехзначный.
Вариант 1: Простое добавление префикса столетия
Если мы точно знаем, что работаем с документами текущего века, можно использовать следующую проверку:
Год = Число(Сред(Остаток, ПозицияТочки2 + 1));
Если Год < 100 Тогда
Год = 2000 + Год; // Превращаем 21 в 2021
КонецЕсли;
ИщемДату = Дата(Год, Число(Месяц), Число(День));
Вариант 2: Универсальный подход с учетом «Окна года»
Более грамотным подходом будет проверка порога (например, если год больше 50, то это 1900-е, если меньше — 2000-е), чтобы код корректно обрабатывал исторические данные или будущие периоды:
Год = Число(ГодИзСтроки);
Если Год < 100 Тогда
Год = Год + ?(Год < 50, 2000, 1900);
КонецЕсли;
Помимо проблемы с датой, существуют и другие технические нюансы, которые могут блокировать поиск в 1С 7.7. Разберем их по порядку, чтобы исключить возможные ошибки.
1. Периодичность нумерации
В конфигураторе для каждого вида документа задается свойство «Периодичность» (Год, Квартал, Месяц, День или Непериодический). Метод НайтиПоНомеру(Номер, Дата) использует второй параметр именно для того, чтобы определить, в каком временном интервале искать указанный номер. Если периодичность установлена в «Год», а вы ошиблись с годом в дате (как в случае с 0021 годом), поиск будет производиться в пустом периоде.
2. Лидирующие нули и длина номера
Номера документов в 1С 7.7 — это строки фиксированной длины. Если в конфигураторе длина номера определена как 10 символов, то номер «ТИ3965» в базе фактически хранится как «ТИ00003965» или «0000ТИ3965» (в зависимости от префикса).
При использовании НайтиПоНомеру() крайне важно передавать строку, точно соответствующую по длине и содержанию номеру в базе данных. Если вы извлекаете номер из текста как «ТИ3965», а в базе он «ТИ00003965», поиск вернет 0. Используйте функции дополнения строки нулями, если это необходимо.
3. Регистр символов и префиксы
Поиск в 1С 7.7 чувствителен к регистру. Номер «ти003965» (строчными) не будет равен номеру «ТИ003965» (прописными). При разборе текста из платежки рекомендуется всегда приводить извлеченный номер к верхнему регистру с помощью функции ВРег().
4. Особенности функции СокрЛП()
Будьте осторожны при использовании СокрЛП(). В строках, скопированных из банковских систем, могут встречаться «невидимые» символы, такие как неразрывный пробел (ASCII код 160). Обычная функция СокрЛП() его не удаляет, так как считает значащим символом. Если такой пробел попадет в конец или начало номера, строковое сравнение внутри метода НайтиПоНомеру() не сработает.
Обобщая все вышесказанное, составим правильный алгоритм для поиска документа по данным из текстовой строки:
СоздатьОбъект("Документ.ИмяВида").НайтиПоНомеру(), передавая подготовленные переменные.Соблюдение этих правил позволит избежать большинства «мистических» ошибок, когда поиск в 1С 7.7 отказывается работать с корректными на первый взгляд данными.