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