При интеграции 1С с банковскими системами (например, Сбербанк Бизнес Онлайн) разработчики часто сталкиваются с необходимостью формирования файлов реестров (реестры на зачисление заработной платы, реестры платежей за ЖКУ и т.д.). Одним из строгих требований большинства банков является уникальность имени каждого отправляемого файла. Сбербанк использует стандартизированные маски имен файлов, где обязательным элементом является уникальный порядковый номер реестра (например, трехзначное число от 001 до 999).
Если отправить файл с именем, которое уже загружалось ранее, банковская система автоматически отклонит его как дубликат. Даже если по регламенту выгрузка происходит один раз в сутки, всегда существует вероятность повторной отправки реестра из-за ошибок в данных или технических сбоев. В этой статье мы подробно рассмотрим, как реализовать надежный автоинкремент номера в имени TXT-файла средствами платформы 1С:Предприятие 8.
Самый простой и очевидный совет, который часто можно встретить на форумах, — жестко прописать в коде номер "001", если выгрузка осуществляется строго один раз в день. Однако на практике этот подход крайне ненадежен по следующим причинам:
Поэтому правильным решением является динамическое определение и увеличение номера файла на +1 при каждой выгрузке.
Если у вас нет возможности или прав на изменение структуры метаданных конфигурации 1С, вы можете определять последний отправленный номер непосредственно по файлам, хранящимся в каталоге выгрузки. Для этого мы будем использовать встроенную функцию 1С НайтиФайлы().
Рассмотрим подробнее алгоритм реализации этого метода:
Посмотрим на пример программной реализации этого метода:
// Определяем каталог и маску поиска
КаталогВыгрузки = "C:\SberbankExport\";
ТекущаяДатаСтр = Формат(ТекущаяДата(), "ДФ=ddMMyyyy"); // Например, 25102023
МаскаПоиска = "*_" + ТекущаяДатаСтр + "_*.txt";
НайденныеФайлы = НайтиФайлы(КаталогВыгрузки, МаскаПоиска);
МаксимальныйНомер = 0;
Для Каждого Файл Из НайденныеФайлы Цикл
ИмяБезРасширения = Файл.ИмяБезРасширения;
// Разбиваем имя файла по разделителю "_"
ЧастиИмени = СтрРазделить(ИмяБезРасширения, "_");
// Предположим, структура имени: ИНН_РС_НОМЕР_ДАТА
// Номер находится на 3-й позиции (индекс 2 в массиве)
Если ЧастиИмени.Количество() >= 3 Тогда
СтрокаНомера = ЧастиИмени[2];
Попытка
ЧислоНомера = Число(СтрокаНомера);
Если ЧислоНомера > МаксимальныйНомер Тогда
МаксимальныйНомер = ЧислоНомера;
КонецЕсли;
Исключение
// Игнорируем файлы, где на месте номера не число
КонецПопытки;
КонецЕсли;
КонецЦикла;
// Вычисляем новый номер реестра
НовыйНомер = МаксимальныйНомер + 1;
Важное замечание: Метод сканирования диска имеет существенный недостаток. Если файлы из каталога будут удалены пользователем, перемещены в архив или переименованы, нумерация неизбежно собьется и начнется заново с 1. Это может привести к конфликту имен на стороне банка.
Чтобы исключить зависимость от состояния файлов на жестком диске, наиболее профессиональным решением является хранение истории выгрузок непосредственно в базе данных 1С. Для этого идеально подходит непериодический независимый Регистр сведений.
Создадим регистр сведений со следующей структурой:
ИсторияВыгрузкиРеестровОрганизация (Тип: СправочникСсылка.Организации)БанковскийСчет (Тип: СправочникСсылка.БанковскиеСчетаОрганизаций)ДатаВыгрузки (Тип: Дата, без времени)НомерРеестра (Тип: Число, длина 3, точность 0)Разберем по шагам, как работать с этим регистром при формировании очередного файла выгрузки. Напишем функцию для получения следующего свободного номера:
Функция ПолучитьСледующийНомерРеестра(Организация, БанковскийСчет) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ИсторияВыгрузки.НомерРеестра КАК НомерРеестра
|ИЗ
| РегистрСведений.ИсторияВыгрузкиРеестров КАК ИсторияВыгрузки
|ГДЕ
| ИсторияВыгрузки.Организация = &Организация
| И ИсторияВыгрузки.БанковскийСчет = &БанковскийСчет
| И ИсторияВыгрузки.ДатаВыгрузки = &ДатаВыгрузки";
Запрос.УстановитьПараметр("Организация", Организация);
Запрос.УстановитьПараметр("БанковскийСчет", БанковскийСчет);
Запрос.УстановитьПараметр("ДатаВыгрузки", НачалоДня(ТекущаяДата()));
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
// Если сегодня выгрузок еще не было, возвращаем начальный номер
Возврат 1;
Иначе
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
Возврат Выборка.НомерРеестра + 1;
КонецЕсли;
КонецФункции
После успешного формирования файла и сохранения его на диск обязательно нужно зафиксировать новый использованный номер в базе данных, чтобы при следующем запуске обработки код выдал правильный результат:
Процедура ЗафиксироватьНомерРеестра(Организация, БанковскийСчет, НомерРеестра) Экспорт
МенеджерЗаписи = РегистрыСведений.ИсторияВыгрузкиРеестров.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Организация = Организация;
МенеджерЗаписи.БанковскийСчет = БанковскийСчет;
МенеджерЗаписи.ДатаВыгрузки = НачалоДня(ТекущаяДата());
// Считываем текущую запись, чтобы перезаписать ее ресурс
МенеджерЗаписи.Прочитать();
МенеджерЗаписи.НомерРеестра = НомерРеестра;
Попытка
МенеджерЗаписи.Записать(Истина);
Исключение
ЗаписьЖурналаРегистрации("ВыгрузкаРеестра", УровеньЖурналаРегистрации.Ошибка,,,
"Не удалось записать номер реестра в БД: " + ОписаниеОшибки());
ВызватьИсключение "Ошибка сохранения номера реестра! Обратитесь к администратору.";
КонецПопытки;
КонецПроцедуры
Если ваша внешняя обработка запускается на основании какого-либо документа системы (например, типового документа ВедомостьНаВыплатуЗарплатыВБанк или вашего специализированного кастомного документа), то изобретать велосипед не нужно. В платформу 1С уже заложен мощный механизм автонумерации документов, гарантирующий уникальность в пределах периода (года или месяца).
В этом случае при выгрузке достаточно просто прочитать номер документа-основания, очистить его от префиксов организации/базы и использовать в качестве порядкового номера для файла выгрузки.
Поскольку банк ожидает строго фиксированную длину номера (обычно это 3 символа, то есть 001, 015, 120), простое преобразование числа в строку функцией Строка(НовыйНомер) вернет нам значения "1" или "15", что вызовет ошибку формата при импорте в банк.
Для правильного приведения чисел к строковому формату с лидирующими нулями в 1С используется функция Формат() со спецификатором ЧВН (Численное Выводить Нули) и указанием длины ЧЦ (Число Цифр):
// Форматируем числовой номер в трехзначную строку с лидирующими нулями
НомерДляБанка = Формат(НовыйНомер, "ЧЦ=3; ЧРД=; ЧГ=0; ЧВН=");
// Результат для числа 5 -> "005"
// Результат для числа 42 -> "042"
Разберем параметры этого формата:
ЧЦ=3 — общая длина числа (3 знака). Если число превысит 999, то формат обрежет его или выдаст ошибку, поэтому важно следить за границей диапазона.ЧРД= — убирает разделитель дробной части.ЧГ=0 — отключает группировку триад цифр пробелами (чтобы не получить "1 00" вместо "100").ЧВН= — включает режим заполнения пустых разрядов лидирующими нулями.В случае, если нумерация сквозная и не сбрасывается ежедневно, количество выгружаемых реестров рано или поздно превысит отметку 999. При попытке преобразовать число 1000 через формат "ЧЦ=3" произойдет сбой формата, либо банк отклонит некорректно сформированное имя файла.
Для решения этой проблемы мы рекомендуем внедрить в алгоритм проверку: при достижении значения 1000 счетчик должен автоматически сбрасываться обратно на 1. Поскольку дата файла при этом будет уже новой, уникальность имени файла на стороне Сбербанка не нарушится.
Рассмотренные нами подходы позволяют гибко решить задачу автоинкремента номеров файлов реестров. Для простых одноразовых задач подойдет сканирование каталога через НайтиФайлы(), а для надежных промышленных решений в крупных компаниях всегда следует делать выбор в пользу хранения истории в специальном Регистре сведений.