Как правильно реализовать отмену проведения документа в 1С, чтобы избежать ошибок валидации?

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

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

Представим, что у нас есть документ «ПриемНаРаботу» и требование: «Нельзя принимать на работу сотрудника два раза, если не было до этого увольнения». Для этого мы реализовали функцию СотрудникПринятНаРаботу(Сотрудник), которая проверяет наличие действующей записи о приеме на работу в регистрах. Эта проверка обычно размещается в процедуре ПередЗаписью документа или в ОбработкаПроведения.

Когда мы пытаемся отменить проведение уже проведенного документа «ПриемНаРаботу» с помощью стандартной кнопки «ОтменаПроведения», система выдает ошибку, например: «Нельзя принять на работу сотрудника, который уже принят». Почему это происходит? Потому что в процессе отмены проведения, перед непосредственным снятием статуса "Проведен", система все равно вызывает процедуру ПередЗаписью, где наша проверка на наличие сотрудника срабатывает, блокируя операцию.

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

Решение 1: Разграничение логики в процедуре ПередЗаписью с помощью параметра РежимЗаписи

Процедура ПередЗаписью документа в 1С вызывается перед каждой операцией записи объекта в базу данных. Это очень мощный инструмент для контроля и валидации. Она имеет стандартные параметры, одним из которых является РежимЗаписи. Этот параметр позволяет нам точно определить, в каком режиме происходит текущая операция записи документа. Он может принимать следующие значения из перечисления РежимЗаписиДокумента:

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

Мы используем этот параметр для того, чтобы наша проверка на дублирование срабатывала только при попытке провести документ, но не при его отмене проведения. Давайте изменим наш код в процедуре ПередЗаписью:


Процедура ПередЗаписью(Отказ, РежимЗаписи)
    // Мы хотим, чтобы проверка "Нельзя принять на работу сотрудника, который уже принят"
    // срабатывала только при попытке провести документ.
    // При отмене проведения или просто записи документа эта проверка не должна выполняться.

    Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
        // Этот блок кода будет выполнен только тогда, когда пользователь
        // нажимает кнопку "Провести" или пытается провести документ программно.

        Если СотрудникПринятНаРаботу(Сотрудник) Тогда
            Сообщить("Нельзя принять на работу сотрудника, который уже принят.");
            Отказ = Истина; // Отменяем проведение документа
        КонецЕсли;
    КонецЕсли;
    // Если РежимЗаписи не равен РежимЗаписиДокумента.Проведение,
    // то данный блок кода пропускается, и документ может быть записан или отменен.
КонецПроцедуры

В этом коде мы обернули нашу проверку условием Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда ... КонецЕсли;. Теперь, когда пользователь нажимает кнопку "ОтменаПроведения", значение параметра РежимЗаписи будет РежимЗаписиДокумента.ОтменаПроведения, и условие РежимЗаписи = РежимЗаписиДокумента.Проведение окажется ложным. Соответственно, проверка на дублирование не будет выполнена, и отмена проведения пройдет успешно (при условии, что нет других ошибок).

Некоторые разработчики могут использовать условие Если РежимЗаписи <> РежимЗаписиДокумента.ОтменаПроведения Тогда. Это тоже будет работать, так как оно охватывает как проведение, так и просто запись. Однако, для данной конкретной задачи (проверка уникальности при приеме на работу), более точным является именно Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда, поскольку мы заинтересованы в валидации именно в момент активного действия по проведению.

Важно помнить об ошибке, которая может возникнуть: "Преобразование значения к типу Булево не может быть выполнено". Это происходит, если вы попытаетесь использовать РежимЗаписиДокумента.ОтменаПроведения как логическое значение напрямую, например, Если НЕ РежимЗаписиДокумента.ОтменаПроведения Тогда. Правильный способ сравнения — всегда использовать операторы сравнения, такие как = или <>, например, Если РежимЗаписи <> РежимЗаписиДокумента.ОтменаПроведения Тогда.

Решение 2: Реализация процедуры ОбработкаОтменыПроведения

Разграничение логики в ПередЗаписью решает проблему блокировки отмены проведения. Однако, это только половина дела. Если мы просто снимем флаг "Проведен" у документа, но не отменим его движения по регистрам, данные в системе останутся неконсистентными. Например, в регистре сведений о сотрудниках все еще будет запись о приеме на работу, хотя документ "ПриемНаРаботу" уже не проведен.

Для корректной отмены движений предназначена специальная процедура — ОбработкаОтменыПроведения. Эта процедура автоматически вызывается платформой, когда пользователь или программный код отменяет проведение документа. Ее основная задача — удалить или скорректировать те записи в регистрах, которые были созданы при проведении документа в процедуре ОбработкаПроведения.

Если мы не реализуем эту процедуру, или реализуем ее некорректно, то при отмене проведения документа движения по регистрам останутся. Это приведет к тому, что наша функция СотрудникПринятНаРаботу(Сотрудник) все равно будет возвращать Истина, поскольку она будет находить запись о приеме на работу, которая фактически не была удалена, хоть документ и "отменен".

Давайте посмотрим, как можно реализовать процедуру ОбработкаОтменыПроведения:


Процедура ОбработкаОтменыПроведения(Отказ, РежимПроведения)
    // В этой процедуре мы отменяем все движения, которые были созданы документом
    // при его проведении. Это гарантирует, что данные в регистрах будут актуальными
    // после отмены проведения документа.

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

    // 2. Если требуется более сложная логика отмены (например, частичное удаление
    // или корректировка, а не полное удаление всех движений), то можно работать
    // с каждым набором записей регистра отдельно.
    // Например, для регистра сведений или накопления:
    // Движения.МойРегистрСведений.Записывать = Истина;
    // Движения.МойРегистрСведений.Очистить(); // Удалит только движения этого регистра
    // 
    // Или, если регистр непериодический и записи можно удалить по отбору:
    // НаборЗаписей = РегистрыСведений.МойРегистрСведений.СоздатьНаборЗаписей();
    // НаборЗаписей.Отбор.Регистратор.Установить(Ссылка);
    // НаборЗаписей.Прочитать();
    // НаборЗаписей.Очистить();
    // НаборЗаписей.Записать();

    // Для нашего примера с "Приемом на работу", если мы используем
    // какой-либо регистр, фиксирующий факт приема, нам нужно убедиться,
    // что записи об этом приеме будут удалены или скорректированы.
    // Если "СотрудникПринятНаРаботу" смотрит в РегистрСведений.СотрудникиОрганизаций (или подобный),
    // то необходимо, чтобы записи, созданные ПриемомНаРаботу, были удалены.
    // Движения.РегистрСведений.СотрудникиОрганизаций.УдалитьПоОтбору(Новый Структура("ДокументОснование", Ссылка));
    // Или, если регистр периодический и факт приема определяется по дате:
    // Движения.РегистрСведений.КадроваяИстория.УдалитьПоОтбору(Новый Структура("Сотрудник", Сотрудник, "ВидСобытия", ВидСобытияПриемНаРаботу), Дата);
    // Важно, чтобы действия здесь были зеркальны действиям в ОбработкаПроведения.

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

Наиболее распространенный и безопасный способ отмены движений, если при проведении документ формирует движения по нескольким регистрам, это использовать Движения.Очистить() в процедуре ОбработкаОтменыПроведения. Этот метод автоматически удаляет все записи, созданные данным документом, во всех регистрах, по которым он делал движения. Если вам нужна более тонкая настройка или выборочное удаление/корректировка, то следует работать с наборами записей конкретных регистров.

Очень важно, чтобы процедура ОбработкаОтменыПроведения была реализована таким образом, чтобы после ее выполнения, функция СотрудникПринятНаРаботу(Сотрудник) корректно отражала факт, что сотрудник больше не числится принятым (если это был единственный документ, подтверждающий его прием). То есть, после отмены проведения документа, вызов СотрудникПринятНаРаботу(Сотрудник) должен возвращать Ложь.

Взаимодействие процедур ПередЗаписью, ОбработкаПроведения и ОбработкаОтменыПроведения

Для полноты картины, давайте рассмотрим, как эти процедуры взаимодействуют друг с другом в жизненном цикле документа:

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

Важность функции СотрудникПринятНаРаботу()

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

Как правило, такая функция обращается к регистрам, в которые документ «ПриемНаРаботу» делает движения. Например, это может быть регистр сведений, хранящий информацию о текущем кадровом состоянии сотрудников. Если ОбработкаПроведения добавляет запись в этот регистр, то СотрудникПринятНаРаботу() будет находить эту запись и возвращать Истина.

Соответственно, после того как ОбработкаОтменыПроведения удалит или скорректирует записи в этом регистре, функция СотрудникПринятНаРаботу() уже не должна находить активную запись о приеме на работу и обязана возвращать Ложь. Это является подтверждением корректности всей реализованной логики.

Если после отмены проведения документа функция все еще возвращает Истина, это означает, что либо ОбработкаОтменыПроведения не сработала должным образом (не удалила нужные записи), либо СотрудникПринятНаРаботу() запрашивает данные из некорректного источника или с неправильными параметрами.

Заключение

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

Одновременно, реализация процедуры ОбработкаОтменыПроведения является обязательной для обеспечения целостности данных в регистрах. Без нее отмена проведения документа будет лишь "визуальной", а фактические данные останутся неизменными, что приведет к логическим ошибкам в системе. Используя эти два механизма в связке, мы можем обеспечить гибкое и корректное поведение документов, соответствующее всем требованиям бизнес-логики.

← На главную