При работе с внешними обработками для обмена данными со Сбербанком (особенно в конфигурациях на обычных формах, таких как «Зарплата и Управление Персоналом 2.5») пользователи часто сталкиваются с внезапным отказом системы. Вчера выгрузка работала исправно, а сегодня система выдает критическую ошибку: «Преобразование значения к типу Число не может быть выполнено». Давайте подробно разберем, почему возникает эта проблема, как устроена логика обмена со Сбербанком и как исправить этот сбой раз и навсегда.
Для начала проанализируем фрагмент кода, на котором происходит аварийное завершение работы программы:
Если Длг.Выбрать() Тогда
НайденныеФайлы = НайтиФайлы(Длг.Каталог, "*.xml");
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Имя");
Для Каждого с Из НайденныеФайлы Цикл
н = ТЗ.Добавить();
н.Имя = с.ИмяБезРасширения;
КонецЦикла;
ТЗ.Сортировать("Имя Убыв");
Если ТЗ.Количество() > 0 Тогда
ПоследнееИмя = ТЗ[0].Имя;
НовоеИмя = Число(Лев(ПоследнееИмя, Найти(ПоследнееИмя, "z")-1));
ИмяФайла = Длг.Каталог + "\" + Строка(Формат(НовоеИмя+1,"ЧГ=")) + "z" + ".xml";
Иначе
ИмяФайла = Длг.Каталог + "\" + "8606269z.xml";
КонецЕсли;
КонецЕсли;
Ошибка локализована в строке вычисления переменной НовоеИмя. Чтобы понять, почему она возникает, нам необходимо обратиться к стандартам формирования имен файлов, которые устанавливает ПАО Сбербанк для зарплатных проектов.
Согласно официальной спецификации Сбербанка по обмену электронными реестрами, имена файлов строятся по строгой маске TTTTNNNM.xml, где каждая группа символов имеет свое значение:
8606).269).Маркеры типа файла могут принимать следующие значения:
z — реестр на зачисление денежных средств (заработной платы) на счета сотрудников.o — реестр на открытие лицевых счетов новым сотрудникам.y — ответный файл от банка (подтверждение выполнения операции) с результатами зачисления.n — ответный файл от банка с результатами открытия счетов.В нашем программном коде разработчик заложил жесткое ожидание того, что в папке будут находиться исключительно файлы реестров на зачисление зарплаты, содержащие букву z. Но реальная практика работы бухгалтера вносит свои коррективы.
Рассмотрим по шагам физический смысл происходящего сбоя:
o.xml или файл ответа банка с суффиксом y.xml (допустим, 8606269y.xml)."*.xml". В таблицу значений попадают абсолютно все xml-файлы в каталоге.8606269y.xml (или файл с другим расширением, оказавшийся выше по алфавиту) попадает на первую строчку таблицы и считывается в переменную ПоследнееИмя.Найти(ПоследнееИмя, "z") пытается отыскать символ «z» в строке "8606269y". Поскольку этой буквы там нет, функция возвращает значение 0.0 - 1 = -1.Лев("8606269y", -1) не приводит к аварийной остановке. Платформа устроена так, что при получении отрицательной длины она просто возвращает пустую строку "".Число(""). Платформа 1С не может преобразовать пустую строку в числовой тип, что и вызывает системное исключение: «Преобразование значения к типу Число не может быть выполнено».Даже если бы бухгалтер строго следил за чистотой каталога и удалял все лишние файлы, данный алгоритм гарантированно сломался бы в будущем. Виной тому строка ТЗ.Сортировать("Имя Убыв");.
Колонка Имя имеет строковый тип данных. Сортировка строковых данных в 1С (как и в любой другой СУБД) происходит посимвольно (по алфавиту), а не по числовому значению. В строковой сортировке:
"86069z" будет признана большей, чем строка "860610z", так как символ «9» идет в кодовой таблице позже символа «1»."86062z" окажется выше, чем "8606115z".Как только порядковый номер реестра увеличится на один разряд (перейдет с однозначного на двузначный, с двузначного на трехзначный и т. д.), сортировка нарушится. Программа выберет в качестве «последнего» файла устаревший реестр с меньшим количеством цифр в номере, увеличит его значение на 1 и попытается записать новый файл. Это приведет к перезаписи существующих важных файлов или к ошибке совместного доступа.
Для исправления алгоритма нам нужно решить три задачи:
z).Рассмотрим, как правильно реализовать эти шаги в коде обработки.
Вместо поиска всех файлов по маске "*.xml", мы применим фильтрацию еще на этапе обращения к файловой системе. Будем искать только те файлы, имя которых оканчивается на символ «z». Для этого заменим маску поиска:
НайденныеФайлы = НайтиФайлы(Длг.Каталог, "*z.xml");
Эта простая мера позволит сразу проигнорировать файлы подтверждения от банка (y.xml) и реестры на открытие счетов (o.xml).
Чтобы сортировка происходила корректно, мы добавим в таблицу значений дополнительную числовую колонку. При обходе найденных файлов мы будем «на лету» выделять числовую часть имени, преобразовывать ее в число и записывать в эту колонку. После этого мы отсортируем таблицу по числовому полю. Такой подход гарантирует, что число 10 всегда будет больше числа 9.
Посмотрим на пример реализации новой логики наполнения и сортировки таблицы значений:
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("ИмяСтрока", Новый ОписаниеТипов("Строка"));
ТЗ.Колонки.Добавить("ЧисловойНомер", Новый ОписаниеТипов("Число"));
Для Каждого ТекущийФайл Из НайденныеФайлы Цикл
ИмяБезРасширения = ТекущийФайл.ИмяБезРасширения;
ПозицияZ = Найти(НРег(ИмяБезРасширения), "z");
// Проверяем, действительно ли в имени файла есть символ "z"
Если ПозицияZ > 1 Тогда
СтрокаЧисла = Лев(ИмяБезРасширения, ПозицияZ - 1);
// Безопасное преобразование строковой части в число
Попытка
ЧисловоеЗначение = Число(СтрокаЧисла);
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.ИмяСтрока = ИмяБезРасширения;
НоваяСтрока.ЧисловойНомер = ЧисловоеЗначение;
Исключение
// Если в имени файла помимо цифр были другие символы, игнорируем его
Продолжить;
КонецПопытки;
КонецЕсли;
КонецЦикла;
// Теперь сортируем таблицу по числовому представлению номера реестра в убывающем порядке
ТЗ.Сортировать("ЧисловойНомер Убыв");
Теперь соберем все наши доработки в единый готовый блок кода. Данный алгоритм защищен от наличия посторонних файлов в папке, не зависит от регистра символов (обрабатывает как латинскую «z», так и возможные ошибки ввода) и гарантирует математически точную сортировку номеров реестров.
Если Длг.Выбрать() Тогда
// Ищем файлы только реестров зачисления зарплаты
НайденныеФайлы = НайтиФайлы(Длг.Каталог, "*z.xml");
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("ИмяСтрока", Новый ОписаниеТипов("Строка"));
ТЗ.Колонки.Добавить("ЧисловойНомер", Новый ОписаниеТипов("Число"));
Для Каждого Файл Из НайденныеФайлы Цикл
ИмяБезРасширения = Файл.ИмяБезРасширения;
// Переводим в нижний регистр для защиты от случайной заглавной "Z"
ИмяНРег = НРег(ИмяБезРасширения);
ПозицияРазделителя = Найти(ИмяНРег, "z");
Если ПозицияРазделителя > 1 Тогда
СтрокаНомера = Лев(ИмяБезРасширения, ПозицияРазделителя - 1);
Попытка
ЧисловойНомер = Число(СтрокаНомера);
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.ИмяСтрока = ИмяБезРасширения;
НоваяСтрока.ЧисловойНомер = ЧисловойНомер;
Исключение
// Файл не соответствует стандартному формату, пропускаем его
Продолжить;
КонецПопытки;
КонецЕсли;
КонецЦикла;
ТЗ.Сортировать("ЧисловойНомер Убыв");
Если ТЗ.Количество() > 0 Тогда
// Берем строку с самым большим числовым номером
МаксимальныйНомер = ТЗ[0].ЧисловойНомер;
НовыйНомер = МаксимальныйНомер + 1;
// Форматируем число, убирая группировку триад (пробелы между тысячами)
ИмяФайла = Длг.Каталог + "\" + Формат(НовыйНомер, "ЧГ=") + "z.xml";
Иначе
// Если папка пуста, создаем стартовый файл с дефолтным номером
ИмяФайла = Длг.Каталог + "\8606269z.xml";
КонецЕсли;
// Дальнейший запуск процедуры записи и формирования XML-структуры...
Иначе
Возврат Неопределено;
КонецЕсли;
Внедрение этой логики полностью решает проблему. Программа больше не прервет работу из-за случайного файла в папке экспорта, а нумерация реестров всегда будет последовательной и корректной.