Рассматривать алгоритм буду на примере Управление торговлей 10.3, механизм можно внедрить практически в любую из типовых конфигураций. Дано:
- большое количество счетов/заказов
- большое количество входящих платежей (оптимистично, да). Это могут быть Платежки, ПКО и прочие приходные документы.
Необходимо автоматически сопоставить платежи с выставленными счетами и указать их в платежных поручениях входящих (или попытаться это сделать).
Начнем с состава метаданных. Для указания такого соответствия в входящих платежных документах существует отдельная табличная часть - РасшифровкаПлатежа, где в строках проставляется, к каким документам этот платеж относится, и делится сумма платежа на составляющие.
На форме таких документов - есть переключатель Без разбиения/Списком - который, по сути, только переключает внешнее представление этой табличной части. В случае выбора варианта Без разбиения - мы работаем только с первой строкой данной табличной части. В случае выбора второго варианта - имеем дело со всей таблицей.
При поступлении платежа по банку, нам доступны для анализа несколько реквизитов. Это - Контрагент, Договор контрагента, Сумма, Назначение платежа (текстовый комментарий при оформлении платежки). Будем их использовать!
Сразу отмечу - можно сделать автоматическое разнесение минимум двумя способами - обработка, которая будет анализировать все поступающие платежи за период, в дальнейшем можно это поместить в регламентное задание. Второй способ - влезем в модуль документов, процедуру ПриЗаписи - способ менее хорош, потому что задействуем изменение конфигурации.
В данной статье рассмотрим второй способ - он проще и нагляднее, как мне кажется, а привести его к первому варианту не составит большого труда.
Итак. Рассмотрим поступление Платежного поручения входящего (ППВ). Обычно их загружают из клиент-банка, реже вводят вручную. Все необходимые нам реквизиты (повторю - это Контрагент, Договор, Сумма, Назначение) вводятся в ППВ. Теперь нам необходимо проанализировать имеющуюся информацию и попытаться найти один или несколько необходимых счетов.
Анализировать предлагаю в процедуре модуля ПередЗаписью - вызывается перед записью и соответственно перед проведением документа и позволит нам заполнить табличную часть РасшифровкаПлатежа.
Для начала проанализируем, может, мы уже указали соответствующий заказ и ничего делать не требуется. Критериев может быть несколько - заполнены ли все строки в РасшифровкеПлатежа, заполнена ли первая строка в этой ТЧ. Как показала практика, достаточно анализа первой строки:
если не ЗначениеЗаполнено(РасшифровкаПлатежа[0].Сделка) тогда
делаем дальше. Для хранения соответствия нам нужна таблица значений, которую мы потом перенесем в ТЧ.
тзн = новый ТаблицаЗначений;
тзн.Колонки.Добавить("Счет");
тзн.Колонки.Добавить("Сумма");
тзн.Колонки.Добавить("ДатаСчета");
Теперь разбираем Назначение платежа и пытаемся вытащить полезную для нас информацию, которая поможет найти необходимые счета или заказы. Проходим последовательно каждые 10 символов в Назначении и проверяем, не дата ли это. Если дата, то добавляем информацию в нашу новую таблицу значений. Прогоняем два раза - иногда бухгалтеры пишут год в полном формате - "2015", а иногда просто "15". Будем искать фрагменты вида ХХ.ХХ.ХХХХ и ХХ.ХХ.ХХ. В конце проверяем - корректная ли найденная дата. При желании можно самостоятельно оптимизировать код для одного прогона цикла.
назн = НазначениеПлатежа;
для к = 1 по стрдлина(назн) - 10 цикл
ск = СтрЗаменить(сред(назн, к,10),"/",".");
дт = сред(ск,7,4)+сред(ск,4,2)+лев(ск,2);
если лев(дт,2) <> "20" тогда
продолжить;
конецесли;
ндт = дата(2000,1,2);
попытка
ндт = дата(дт);
исключение
конецпопытки;
если ндт <> дата(2000,1,2) и ндт <= дата+24*60*60 тогда
новстр = тзн.Добавить();
новстр.датасчета = ндт;
конецесли;
конеццикла;
для к = 1 по стрдлина(назн) - 10 цикл
ск = СтрЗаменить(сред(назн, к,8),"/",".");
дт = "20"+сред(ск,7,2)+сред(ск,4,2)+лев(ск,2);
ндт = дата(2000,1,2);
попытка
ндт = дата(дт);
исключение
конецпопытки;
если ндт <> дата(2000,1,2) и ндт <= дата+24*60*60 тогда
новстр = тзн.Добавить();
новстр.датасчета = ндт;
конецесли;
конеццикла;
Даты, когда выставлены счета, мы нашли. Теперь проанализируем, есть ли в базе такие счета, и совпадёт ли их сумма с суммой платежа. Если совпадет - бинго! - мы заполнили расшифровку платежа
нашли = ложь;
для каждого стр из тзн цикл
сч = Документы.СчетНаОплатуПокупателю.Выбрать(НачалоДня(стр.ДатаСчета), КонецДня(стр.ДатаСчета), новый структура("Контрагент", Контрагент));
если сч.Следующий() тогда
стр.счет = сч.ссылка;
стр.сумма = сч.СуммаДокумента;
конецесли;
сч = Документы.ЗаказПокупателя.Выбрать(НачалоДня(стр.ДатаСчета), КонецДня(стр.ДатаСчета), новый структура("Контрагент", Контрагент));
если сч.Следующий() тогда
стр.счет = сч.ссылка;
стр.сумма = сч.СуммаДокумента;
конецесли;
конеццикла;
если СуммаДокумента = тзн.итог("Сумма") тогда
расшифровкаплатежа.очистить();
для каждого стр из тзн цикл
новстр = РасшифровкаПлатежа.Добавить();
новстр.ДоговорКонтрагента = ДоговорКонтрагента;
новстр.КратностьВзаиморасчетов = 1;
новстр.КурсВзаиморасчетов = 1;
новстр.СуммаВзаиморасчетов = стр.сумма;
новстр.СуммаПлатежа = стр.сумма;
новстр.сделка = стр.счет;
конеццикла;
конецесли;
Таким образом, как показывает практика, мы сможем заполнить около 80% входящих платежек и как минимум освободим много времени у нашего бухгалтера. Договор при поиске мы не используем, в случае неправильного указания в клиент-банке подходящего договора, мы не сможем найти необходимые счета. Можно вывести предупреждение о разных договорах в найденных счетах и в самом платежном поручении.
Платежные поручения, разнесенные по Заказам, мы у себя в базе, например, выделяем цветом в общем журнале - чтобы можно было сразу видеть, где надо еще поработать руками:
В качестве дополнения - можно научить определять программу месяц в дате по названию - январь, февраль... но на практике такое случается достаточно редко, не увидел необходимости.
Всем спасибо за внимание! Будут предложения по алгоритму - с удовольствием выслушаю :)