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