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