Кастомизация процесса загрузки банковских выписок в конфигурации «1С:Бухгалтерия предприятия 3.0» (БП 3.0) — одна из наиболее распространенных задач для разработчика 1С. Стандартный механизм сопоставления аналитики (контрагентов, договоров и статей движения денежных средств) далеко не всегда удовлетворяет требованиям бизнеса. Часто возникает необходимость реализовать интеллектуальный поиск договора по уникальным идентификаторам, номерам сделок или специфическим ключевым словам из назначения платежа.
Однако при попытке доработать этот узел программисты часто сталкиваются со сложной архитектурной организацией обработки КлиентБанк. Процесс разбит на клиентские и серверные процедуры, фоновые задания, а также работу со специальными промежуточными структурами данных. В этой статье мы подробно разберем, как устроен этот механизм изнутри, и определим точные места в коде для встраивания вашей собственной логики.
Прежде чем вносить изменения в код, проанализируем цепочку событий, которая происходит при импорте текстового файла выписки. Механизм загрузки устроен по двухэтапной схеме:
КлиентБанк, где пользователь может вручную изменить реквизиты перед созданием документов в информационной базе.Рассмотрим подробнее первый этап. Точкой входа является модуль менеджера обработки КлиентБанк, в частности метод ЗагрузитьБанковскиеВыписки. Он инициирует запуск фонового задания.
Непосредственное чтение текстового файла выписки и его разбор осуществляются на сервере в функции СоздатьДокументы общего модуля ЗагрузкаВыпискиПоБанковскомуСчету. В результате парсинга создается специальная промежуточная структура — РаспознанныеДанныеИзБанка. Она содержит таблицы с операциями, метаданными и временными идентификаторами. Эта структура сериализуется и помещается во временное хранилище, откуда затем считывается формой обработки.
Для того чтобы сопоставить входящую строку из файла с объектами базы данных (справочники Контрагенты, БанковскиеСчетаКонтрагентов, ДоговорыКонтрагентов), система использует механизмы двух ключевых общих модулей: ЗагрузкаВыпискиПоБанковскомуСчету и ИдентификацияУчастниковБанковскихОпераций.
Этот процесс можно разделить на два основных шага.
Прежде чем искать объекты в базе данных, программа собирает все возможные текстовые и числовые признаки платежа. Главным местом концентрации этой логики является процедура ЗаполнитьИдентификаторыУчастниковОперацийПоДаннымИзБанка общего модуля ЗагрузкаВыпискиПоБанковскомуСчету.
Именно здесь система анализирует поле файла «Назначение платежа» и пытается извлечь из него номера счетов или договоров. Для выполнения этой задачи вызываются вспомогательные функции, например:
ИдентификацияУчастниковБанковскихОпераций.ИдентификаторПоНомеруСчетаНаОплатуИлиДоговору
Если ваша задача состоит в том, чтобы научить систему распознавать нестандартные шаблоны номеров договоров (например, регулярные выражения вида "Дг-000123" или "Сделка №..."), то дорабатывать алгоритм извлечения поисковых признаков нужно именно в этой процедуре.
После того как поисковые идентификаторы сформированы, система обращается к базе данных для сопоставления объектов:
РеквизитыНовыхОбъектовИнформационнойБазы(Тип) общего модуля ИдентификацияУчастниковБанковскихОпераций, заполняя реквизиты по ИНН/КПП.Данные, которые пользователь видит в табличной части обработки КлиентБанк, формируются на клиенте, но их первоисточником является структура РаспознанныеДанныеИзБанка, полученная из временного хранилища.
В модуле формы обработки КлиентБанк за наполнение таблицы на экране отвечает процедура ЗаполнитьДокументыКИмпортуПоРаспознаннымДаннымИзБанка. Она переносит информацию из серверной структуры в строки таблицы формы.
Важно понимать: если пользователь интерактивно (вручную) меняет договор или контрагента в таблице формы, система выполняет сложную процедуру пересчета хозяйственной операции на сервере. Для этого вызывается функция:
ЗагрузкаВыпискиПоБанковскомуСчету.УточнитьХозяйственнуюОперациюДаннымиВведеннымиПользователем
Она возвращает актуализированную структуру ХозяйственнаяОперация, куда принудительно записывается новое значение:
ХозяйственнаяОперация.Вставить("ДоговорКонтрагента", <ВыбранныйДоговор>)
После этого обновленная структура РаспознанныеДанныеИзБанка повторно записывается во временное хранилище, а строка на форме визуально обновляется через вызов клиентской процедуры ЗаполнитьСтрокуПоХозяйственнойОперации.
Очень часто автоподбор договора сбоит из-за того, что система некорректно классифицирует сам Вид хозяйственной операции (например, вместо «Оплата от покупателя» устанавливает «Поступление по платежным картам»). Если вид операции определен неверно, то договор с видом «С покупателем» физически не сможет подставиться в документ, так как сработает жесткое ограничение типов.
Определение вида операции происходит в общем модуле ЗагрузкаВыпискиПоБанковскомуСчету в функции ХозяйственнаяОперация. Алгоритм базируется на таблице правил ПравилаОпределенияХозяйственныхОпераций и обращении к общему модулю БанковскиеПравила. Программа анализирует первые 5 цифр расчетного счета контрагента (балансовый счет) и ключевые маркеры в назначении платежа.
Рассмотрим практический пример. Предположим, нам необходимо реализовать следующую задачу: если в назначении платежа встречается фраза «По договору № ХХХХ», система должна найти договор по его номеру в базе данных и автоматически подставить его в загружаемый документ, даже если этот договор не помечен как основной.
Наиболее технологичным и безопасным способом решения этой задачи является использование расширения конфигурации. Разберем по шагам два возможных сценария доработки.
Если вам нужно переопределить договоры непосредственно перед тем, как пользователь увидит таблицу на форме обработки, проще всего перехватить управление в процедуре формы ЗаполнитьДокументыКИмпортуПоРаспознаннымДаннымИзБанка. Напишем код обработчика в расширении:
&После("ЗаполнитьДокументыКИмпортуПоРаспознаннымДаннымИзБанка")
Процедура Расш1_ЗаполнитьДокументыКИмпортуПоРаспознаннымДаннымИзБанка(РаспознанныеДанные)
// Обходим строки таблицы документов, подготовленных к импорту на форме
Для Каждого СтрокаДокумента Из Объект.ДокументыКИмпорту Цикл
// Нас интересуют только те строки, где контрагент определен, а договор пустой
Если ЗначениеЗаполнено(СтрокаДокумента.Контрагент) И Не ЗначениеЗаполнено(СтрокаДокумента.Договор) Тогда
// Анализируем текст назначения платежа, полученный из выписки
НазначениеПлатежа = СтрокаДокумента.НазначениеПлатежа;
НомерДоговора = ПолучитьНомерДоговораИзТекста(НазначениеПлатежа);
Если ЗначениеЗаполнено(НомерДоговора) Тогда
// Ищем договор в базе по контрагенту и номеру
НайденныйДоговор = Справочники.ДоговорыКонтрагентов.НайтиПоРеквизиту("Номер", НомерДоговора, , СтрокаДокумента.Контрагент);
Если ЗначениеЗаполнено(НайденныйДоговор) Тогда
// Устанавливаем найденный договор в строку формы
СтрокаДокумента.Договор = НайденныйДоговор;
// Важно! Обновляем кэш распознанных данных в структуре на сервере
// Чтобы при записи документов программа увидела наши изменения
УточнитьДоговорВСерверномКэше(СтрокаДокумента.ИдентификаторОперации, НайденныйДоговор);
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Если вы хотите, чтобы подбор происходил на самом раннем этапе (в фоновом задании) и в структуру РаспознанныеДанныеИзБанка сразу записывался корректный договор, необходимо расширить процедуру ЗаполнитьИдентификаторыУчастниковОперацийПоДаннымИзБанка общего модуля ЗагрузкаВыпискиПоБанковскомуСчету.
Посмотрим на пример перехвата этой процедуры:
&После("ЗаполнитьИдентификаторыУчастниковОперацийПоДаннымИзБанка")
Процедура Расш1_ЗаполнитьИдентификаторыУчастниковОперацийПоДаннымИзБанка(ДанныеВыписки, ИдентификаторыОпераций)
// ДанныеВыписки — это структура, прочитанная напрямую из файла
// ИдентификаторыОпераций — это коллекция, куда система складывает найденные ключи сопоставления
Для Каждого СтрокаОперации Из ДанныеВыписки.ТаблицаОпераций Цикл
НазначениеПлатежа = СтрокаОперации.НазначениеПлатежа;
// Применяем собственный алгоритм парсинга текста назначения платежа
НомерДоговора = ВыделитьНомерДоговора(НазначениеПлатежа);
Если ЗначениеЗаполнено(НомерДоговора) Тогда
// Находим строку идентификаторов для текущей операции
СтрокаИдентификаторов = ИдентификаторыОпераций.Найти(СтрокаОперации.КлючСвязи, "КлючСвязи");
Если СтрокаИдентификаторов <> Неопределено Тогда
// Записываем найденный номер договора в структуру идентификаторов
// Системный поиск примет этот номер во внимание при сопоставлении объектов базы
СтрокаИдентификаторов.НомерДоговора = НомерДоговора;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
При использовании Варианта Б логика подбора органично встраивается в типовой конвейер обработки данных. Программа выполнит поиск договора по переданному номеру стандартными средствами, и на форму выгрузятся уже полностью готовые и корректно сопоставленные данные.
Таким образом, понимание внутренней кухни механизма КлиентБанк в БП 3.0 позволяет решать задачи интеграции любой сложности с минимальным вмешательством в типовой код конфигурации, сохраняя возможность легкого обновления системы в будущем.