Разберем ситуацию, с которой периодически сталкиваются разработчики 1С: пользователи жалуются, что при групповом перепроведении документов из списка (например, по правой кнопке мыши) результат движений по регистрам получается один, а если открыть документ и нажать кнопку «Провести и закрыть» — совершенно другой. В итоге возникает задача программно провести целый массив документов, эмулируя открытие формы и нажатие той самой кнопки «ОК».
Давайте подробно проанализируем, почему возникает эта проблема, как ее решить "в лоб" с помощью вызова методов формы, и как правильно исправить ситуацию архитектурно.
Выясним причину такого поведения. Фундаментальное отличие заключается в том, какие обработчики событий запускает платформа при различных способах записи.
Объект.Записать() или проводим документы прямо из динамического списка, отрабатывают только серверные события модуля объекта: ПередЗаписью, ПриЗаписи и ОбработкаПроведения.ОбработкаПроверкиЗаполненияНаСервере, ПередЗаписьюНаСервере, ПриЗаписиНаСервере и ПослеЗаписиНаСервере.Если предыдущие программисты (или авторы сторонней доработки) поместили логику заполнения реквизитов, расчета сумм или изменения статусов в обработчики формы, то при фоновом проведении этот код просто не выполнится. Отсюда и берется разница в результатах. Для наглядного исследования порядка вызова обработчиков можно использовать разработку для изучения событий платформы 1С.
Кроме того, при интерактивном проведении из формы платформа сама определяет режим проведения (оперативный или неоперативный) в зависимости от текущего времени и даты документа. При программном коде мы обычно жестко задаем этот параметр, что тоже может влиять на контроль остатков.
Если времени на рефакторинг чужого кода нет и нужно срочно провести документы так, как это делает пользователь, рассмотрим программное использование контекста формы. Чтобы имитировать нажатие кнопки «Провести и закрыть», нам нужно вызвать метод Записать() самой формы, передав туда необходимые параметры, а затем закрыть окно.
Посмотрим на пример кода, как это можно реализовать для открытой формы:
ПараметрыЗаписи = Новый Структура();
ПараметрыЗаписи.Вставить("РежимЗаписи", РежимЗаписиДокумента.Проведение);
Попытка
Успех = Форма.Записать(ПараметрыЗаписи);
Если Успех Тогда
Форма.Закрыть();
КонецЕсли;
Исключение
Сообщить("Ошибка при записи документа: " + ОписаниеОшибки());
КонецПопытки;
Если стоит задача обработать массив документов без их визуального вывода на экран, мы можем получить форму программно в фоновом режиме:
Для Каждого СсылкаНаДокумент Из МассивДокументов Цикл
ПараметрыФормы = Новый Структура("Ключ", СсылкаНаДокумент);
ФормаДок = ПолучитьФорму("Документ.РеализацияТоваровУслуг.ФормаОбъекта", ПараметрыФормы);
ПараметрыЗаписи = Новый Структура("РежимЗаписи", РежимЗаписиДокумента.Проведение);
Попытка
ФормаДок.Записать(ПараметрыЗаписи);
Исключение
// Обработка исключений
КонецПопытки;
КонецЦикла;
Важное замечание: Метод ПолучитьФорму инициализирует весь контекст формы со всеми реквизитами и интерфейсными элементами. Запуск этого кода в цикле для большого массива документов будет работать крайне медленно и потребует значительных ресурсов оперативной памяти.
Разберем по шагам, как избавиться от этой проблемы навсегда. Наличие бизнес-логики (заполнение данных, влияние на проводки) в модуле формы — это грубое нарушение стандартов разработки 1С. Вся логика должна находиться в модуле объекта или менеджера. Для выявления и исправления подобных отступлений от правил подойдёт обработка для проверки кода на соответствие стандартам 1С.
Чтобы исправить ситуацию, проанализируем текущий алгоритм:
ПередЗаписьюНаСервере и ПриЗаписиНаСервере.ЭтотОбъект (в модуле объекта) при записи из формы и при записи программно. Это поможет выявить, какие реквизиты дозаполняются магическим образом.ПередЗаписью или ОбработкаПроведения.После того как логика будет перенесена на уровень объекта, разница между кнопкой в форме и перепроведением из списка исчезнет. Вы сможете использовать стандартный, быстрый и оптимизированный серверный код для обработки массивов данных:
Для Каждого ТекДокумент Из МассивДокументов Цикл
ОбъектДок = ТекДокумент.ПолучитьОбъект();
Попытка
ОбъектДок.Записать(РежимЗаписиДокумента.Проведение);
Исключение
Сообщить("Не удалось провести: " + ТекДокумент);
КонецПопытки;
КонецЦикла;
Таким образом, использование методов формы для записи — это временный "костыль" для решения срочной задачи. Стратегически правильным решением всегда остается нормализация кода и перенос бизнес-логики в модуль объекта.