При разработке внешних обработок заполнения документов (например, Отчета о розничных продажах) программисты часто сталкиваются с ситуацией, когда один и тот же алгоритм идеально отрабатывает в файловой базе данных, но наотрез отказывается искать элементы в клиент-серверном варианте (на SQL). Симптомы обычно одинаковы: в файловой базе поиск по коду возвращает ссылку на номенклатуру, а в серверной — пустую ссылку, при этом отладчик на сервере по умолчанию не останавливается на точках останова, усложняя диагностику.
В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем влияние региональных настроек операционной системы сервера на форматирование данных в 1С, рассмотрим особенности контекста выполнения кода и предоставим надежные варианты решения этой задачи.
Выясним, как формируется код номенклатуры при импорте. Очень часто исходные данные считываются из внешних текстовых файлов (CSV, TXT) или XML. Если код в файле представлен в виде числа (например, 12345), разработчики нередко преобразуют его следующим образом:
Код = Строка(Число(Спис[7]));
Рассмотрим, что происходит при выполнении этой строки кода в файловом и серверном режимах:
Строка(12345) вернет строковое значение "12 345". Обратите внимание, что этот пробел является специальным неразрывным пробелом (символ с кодом 160). Если в справочнике «Номенклатура» коды заводились вручную или загружались аналогичным образом, то код элемента в базе данных также будет содержать этот неразрывный пробел ("12 345"). Поиск завершается успешно, так как строки полностью совпадают.ragent на сервере под учетной записью (например, usr1cv8). Серверные операционные системы (особенно на базе Linux или Windows Server с дефолтными английскими настройками локали) часто используют другие стандарты форматирования чисел. Функция Строка(12345) на сервере может вернуть строковое значение без пробелов ("12345") или с запятой в качестве разделителя ("12,345").Когда сервер пытается выполнить поиск элемента методом НайтиПоКоду("12345"), СУБД сравнивает это значение с реальным кодом в базе данных, который записан как "12 345" (с неразрывным пробелом). Естественно, сервер сообщает, что элемент не найден.
Проанализируем требования платформы 1С к выполнению запросов и поиску в базах данных. Метод Справочники.Номенклатура.НайтиПоКоду() относится к объектной технике работы с базой данных. Согласно стандартам разработки 1С, подобные методы недоступны непосредственно на клиенте в управляемом приложении. Они должны выполняться строго в серверном контексте (процедуры с директивой &НаСервере или &НаСервереБезКонтекста).
В файловом режиме платформа эмулирует работу клиента и сервера в одном процессе, поэтому некоторые контекстные ошибки могут не приводить к аварийному завершению работы, а лишь выдавать предупреждения. В полноценном клиент-серверном режиме вызов серверных методов из неверного контекста либо приводит к ошибке компиляции, либо возвращает пустые результаты.
Разберем еще один нюанс. Длина кода в справочнике «Номенклатура» в типовых конфигурациях обычно фиксированная (например, 11 символов). Если код элемента состоит из меньшего количества символов (например, 5 символов "12345"), то СУБД может хранить его, дополняя пробелами справа до полной длины: "12345 ".
Различные СУБД (MS SQL Server, PostgreSQL, файловая СУБД) по-разному обрабатывают концевые пробелы при сравнении строк. MS SQL Server при стандартных настройках сопоставления (Collation) может автоматически отсекать концевые пробелы при поиске, тогда как PostgreSQL или локальная файловая база данных будут требовать точного совпадения строки с учетом всех концевых пробелов.
---
Рассмотрим правильную методику программного поиска номенклатуры по коду, которая гарантирует стабильную работу алгоритма независимо от режима работы базы данных (файловый/серверный) и операционной системы.
Прежде всего убедимся, что чтение файла и поиск элементов выполняются строго в серверном контексте. Создадим на сервере вспомогательную функцию, которая будет принимать очищенный код и возвращать ссылку на найденную номенклатуру.
&НаСервере
Функция НайтиНоменклатуруПоКодуНаСервере(Знач ИсходныйКод) Экспорт
// Очищаем код от возможных случайных пробелов в начале и конце строки
ОчищенныйКод = СокрЛП(ИсходныйКод);
// Выполняем поиск по справочнику
СсылкаНаТовар = Справочники.Номенклатура. НайтиПоКоду(ОчищенныйКод);
Возврат СсылкаНаТовар;
КонецФункции
Категорически откажемся от конструкции Строка(Число()). Чтобы преобразовать считанное из файла число в строку без использования системных региональных настроек разделения триад, применим функцию Формат со специальным параметром группировки ЧГ=0 (Группировка = 0). Это гарантирует получение «чистой» строки на любом сервере:
// Неправильно:
// Код = Строка(Число(Спис[7]));
// Правильно:
ЧисловойКод = Число(Спис[7]);
Код = Формат(ЧисловойКод, "ЧГ=0");
Еще один профессиональный способ быстрого и надежного преобразования числа в каноническую строку без форматирования — использование функции XMLСтрока:
ЧисловойКод = Число(Спис[7]);
Код = XMLСтрока(ЧисловойКод);
Если в вашей базе данных коды номенклатуры уже были записаны с ошибками (содержат неразрывные пробелы), нам необходимо предусмотреть их обработку в коде поиска. Неразрывный пробел в 1С можно получить программно с помощью предопределенного свойства Символы.НПП или функции Символ(160). Напишем алгоритм очистки:
&НаСервере
Функция БезопасныйПоискПоКоду(Знач ПолученныйКод)
// 1. Пробуем найти код в том виде, в котором он пришел
НайденнаяСсылка = Справочники.Номенклатура.НайтиПоКоду(ПолученныйКод);
// 2. Если не нашли, пробуем удалить все виды пробелов (обычные и неразрывные)
Если НайденнаяСсылка.Пустая() Тогда
// Удаляем неразрывные пробелы (код символа 160)
КодБезПробелов = СтрЗаменить(ПолученныйКод, Символы.НПП, "");
// Дополнительно удаляем обычные пробелы (код символа 32)
КодБезПробелов = СтрЗаменить(КодБезПробелов, " ", "");
НайденнаяСсылка = Справочники.Номенклатура.НайтиПоКоду(КодБезПробелов);
КонецЕсли;
Возврат НайденнаяСсылка;
КонецФункции
В некоторых конфигурациях коды номенклатуры имеют текстовый формат с лидирующими нулями (например, "00000012345" вместо "12345"). Метод НайтиПоКоду требует точного совпадения длины строки. Добавим дополнение кода нулями до нужной длины, определяя длину кода динамически из метаданных:
&НаСервере
Функция НайтиКодСЛидирующимиНулями(Знач ИсходныйКод)
ДлинаКода = Метаданные.Справочники.Номенклатура.ДлинаКода;
ОчищенныйКод = СокрЛП(ИсходныйКод);
// Дополняем строку нулями слева до требуемой длины справочника
ФорматированныйКод = Прав("00000000000000" + ОчищенныйКод, ДлинаКода);
Возврат Справочники.Номенклатура.НайтиПоКоду(ФорматированныйКод);
КонецФункции
---
Поскольку проблема возникает в серверном режиме работы, нам жизненно необходимо настроить отладчик для остановки на строках кода, выполняемых на сервере. По умолчанию отладка серверного кода отключена.
Выполним следующие шаги для включения отладки:
regedit в Windows).HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\1C:Enterprise 8.3 Server Agent... (имя ветки зависит от версии платформы).ImagePath, который содержит строку запуска службы ragent.exe."C:\Program Files\1cv8\8.3.21.1624\bin\ragent.exe" -srvc -agent -regport 1541 -port 1540 -range 1560:1591 -d "C:\Program Files\1cv8\srvinfo" -debugПосле этого откроем Конфигуратор, перейдем в меню Отладка -> Подключение... и в списке доступных предметов отладки выберем серверные процессы вашей информационной базы. Теперь точки останова в серверных функциях будут успешно срабатывать.
Применяя указанные методы приведения типов и контролируя контекст выполнения кода, мы навсегда исключим ошибки поиска данных при переносе разработок из файловых баз в клиент-серверные окружения.