Как изменить документ и отменить проведение в событии ПередЗаписью в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке прикладных решений в 1С нередко возникает задача скорректировать данные документа непосредственно в момент его сохранения. Иногда бизнес-логика требует не просто прервать транзакцию с ошибкой, а автоматически исправить данные: удалить некорректные строки из табличной части, сохранить изменения в базе данных, но при этом отменить само проведение. Разберем, как платформа 1С обрабатывает события записи, как правильно управлять режимом записи объекта и с какими архитектурными подводными камнями можно столкнуться при реализации такого сценария.

Механизм подмены режима записи в модуле объекта

Проанализируем стандартный событийный цикл записи документа. Когда пользователь нажимает кнопку проведения, платформа инициирует транзакцию и последовательно вызывает обработчики модуля объекта: сначала ПередЗаписью, затем физически сохраняет реквизиты и строки табличных частей в таблицы СУБД, после чего вызывает ПриЗаписи и ОбработкаПроведения.

Обработчик ПередЗаписью принимает параметры по ссылке:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    // Параметр РежимЗаписи доступен для изменения
КонецПроцедуры

Параметр РежимЗаписи определяет намерение платформы: будет ли документ просто записан (РежимЗаписиДокумента.Запись), проведен (РежимЗаписиДокумента.Проведение) или снят с проведения (РежимЗаписиДокумента.ОтменаПроведения). Если мы изменим значение этой переменной прямо внутри обработчика, платформа скорректирует дальнейшее поведение транзакции.

Рассмотрим рабочий пример корректировки табличной части с отменой проведения:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
        
        // Выполняем проверку и удаление строк
        МассивУдаляемыхСтрок = Новый Массив;
        Для Каждого Стр Из Товары Цикл
            Если Не Стр.ПрошлаКонтроль Тогда
                МассивУдаляемыхСтрок.Добавить(Стр);
            КонецЕсли;
        КонецЦикла;
        
        Если МассивУдаляемыхСтрок.Количество() > 0 Тогда
            Для Каждого Стр Из МассивУдаляемыхСтрок Цикл
                Товары.Удалить(Стр);
            КонецЦикла;
            
            // Отменяем проведение, но сохраняем изменения в документе
            Если Проведен Тогда
                РежимЗаписи = РежимЗаписиДокумента.ОтменаПроведения;
            Иначе
                РежимЗаписи = РежимЗаписиДокумента.Запись;
            КонецЕсли;
        КонецЕсли;
        
    КонецЕсли;

КонецПроцедуры

Критически важный момент: ни в коем случае нельзя вызывать метод ЭтотОбъект.Записать() внутри процедуры ПередЗаписью. Подобный вызов порождает рекурсивный запуск событийного цикла и приводит к зацикливанию системы или аварийному завершению сеанса с ошибкой переполнения стека. Платформа самостоятельно сохранит измененные реквизиты и строки при выходе из процедуры.

Почему нельзя модифицировать данные в процедуре ОбработкаПроведения

Иногда разработчики пытаются реализовать изменение реквизитов или удаление строк внутри процедуры ОбработкаПроведения. Выясним, почему такой подход технически ошибочен:

  1. На этапе вызова ОбработкаПроведения сам документ и все его табличные части уже физически записаны в базу данных. В этой процедуре платформа ожидает исключительно формирования наборов записей регистров (движений).
  2. Любые изменения реквизитов объекта или строк табличных частей, выполненные в обработчике проведения, просто потеряются в оперативной памяти и не будут записаны в таблицы СУБД.
  3. Попытка принудительно вызвать метод Записать() внутри проведения приведет либо к исключению блокировки транзакции, либо к непредсказуемым ошибкам целостности данных.

Проблема ранее проведенных документов и зависших движений

Разберем ситуацию, когда пользователь перепроводит документ, который уже имел признак Проведен = Истина. Если в процедуре ПередЗаписью просто переопределить РежимЗаписи = РежимЗаписиДокумента.Запись, платформа снимет признак проведения, однако старые движения по регистрам могут остаться в базе (поведение зависит от свойства документа «Удаление движений»).

В результате возникает расхождение: документ в интерфейсе отображается как непроведенный, но его старые записи по-прежнему учитываются в отчетах по остаткам и оборотам. Чтобы избежать этой коллизии, мы должны обязательно анализировать исходный статус документа: если документ уже был проведен, режим следует переключать на РежимЗаписиДокумента.ОтменаПроведения.

Рассинхронизация клиентской формы и фоновые риски

При модификации данных на стороне сервера всегда необходимо учитывать клиент-серверное взаимодействие и регламентные сценарии:

  1. Рассинхронизация формы: если запись была инициирована из открытой управляемой формы, удаление строк на сервере в модуле объекта не обновляет форму автоматически. На клиенте в таблице останутся старые данные. Если пользователь нажмет кнопку сохранения повторно, клиентская форма перезапишет базу исходным списком строк. Для предотвращения этого требуется оповещать форму или вызывать метод Прочитать() после записи.
  2. Неожиданное поведение при перепроведении: если документы перепроводятся групповой обработкой, процедурой закрытия месяца или восстановлением последовательности, автоматическая модификация документа превратит проведенные документы в непроведенные. Это крайне опасно при перепроведении закрытых периодов.
  3. Невозможность диалога с пользователем: в модуле объекта на сервере нельзя задать интерактивный вопрос через ПоказатьВопрос. Интерактивные подтверждения («Разделить документ?») должны выполняться исключительно на клиенте в модуле формы.

Архитектурно правильные альтернативы

Если задача заключается в разделении одного документа на два (например, перемещение непоместившихся строк в новый документ), перенос этой бизнес-логики в модуль объекта является антипаттерном. Рассмотрим более надежные пути решения:

  1. Обработка на клиенте перед отправкой на сервер: в обработчике формы ПередЗаписьюНаСервере или через специальную клиентскую команду проверяем остатки. При необходимости задаем пользователю вопрос, удаляем строки из данных формы, программно создаем второй документ и только после этого штатно проводим оба объекта.
  2. Использование структуры ДополнительныеСвойства: для защиты от фонового изменения документа при групповых перепроведениях можно передавать специальный признак: Объект.ДополнительныеСвойства.Вставить("ИнтерактивнаяКорректировка", Истина). В модуле объекта алгоритм модификации будет срабатывать только при наличии этого флага.
  3. Ввод на основании или мастер разделения: создание специализированной обработки или штатного механизма ввода на основании, где пользователь осознанно видит, какие позиции переносятся во второй документ, и контролирует проведение обоих документов по отдельности.
← На главную