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