Как программно записать документ в 1С v7.7 и выполнить проверки из ПриЗаписи()?

Программист 1С v7.7
← На главную

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

Давайте подробно рассмотрим, почему так происходит, и какие существуют эффективные способы решения данной проблемы, чтобы обеспечить выполнение всех необходимых проверок, заложенных в ПриЗаписи(), при любой форме записи документа – как интерактивной, так и программной.

Почему ПриЗаписи() не вызывается при программном вызове Записать()?

Выясним причину такого поведения. Процедура ПриЗаписи() является предопределенной процедурой модуля формы. Ее основное назначение — обработка интерактивных действий пользователя, то есть она срабатывает, когда пользователь явно нажимает кнопку "Записать" или "Сохранить" на форме документа. В этот момент система 1С имитирует интерактивное действие, инициируя вызов ПриЗаписи().

Когда же мы используем метод Записать() программно, например, как Документ.Записать() или через метод формы, вызванный из другого участка кода, система выполняет сохранение данных в информационную базу напрямую. В этом случае интерактивное действие не имитируется, и, следовательно, предопределенная процедура ПриЗаписи() не вызывается автоматически.

Эта особенность платформы 1С v7.7 часто ставит в тупик, так как основные проверки и логика отмены записи обычно сосредоточены именно в ПриЗаписи(). Если мы просто вызовем Записать() программно, эти проверки будут проигнорированы, что может привести к сохранению некорректных данных или нарушению бизнес-логики.

Механизм работы СтатусВозврата() и его роль в решении проблемы

Для того чтобы решить проблему невызова ПриЗаписи() и корректно обрабатывать проверки, нам понадобится глубже понять механизм работы функции СтатусВозврата().

Функция СтатусВозврата() в 1С v7.7 предназначена для получения или установки статуса возврата предопределенной процедуры. По умолчанию, при начале выполнения любого участка кода (включая модальные вызовы), СтатусВозврата() возвращает 1. Это означает, что системное действие (например, запись, проведение, открытие формы) должно продолжаться.

Если в теле предопределенной процедуры (или в любой другой процедуре/функции, вызванной из нее) мы установим СтатусВозврата(0), это просигнализирует системе о необходимости отмены текущего системного действия. Например, если в ПриЗаписи() мы установим СтатусВозврата(0), то интерактивная запись документа будет отменена.

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

Документация 1С v7.7 указывает, что устанавливать СтатусВозврата() имеет смысл только в теле предопределенных процедур. Однако на практике она прекрасно отрабатывает свои функции и в пользовательских процедурах, вызванных из предопределенных, что делает ее мощным инструментом для управления системными действиями.

Решение проблемы: имитация интерактивной записи с проверками

Теперь, когда мы понимаем причину и инструмент, рассмотрим, как можно решить проблему с программной записью документов. Существует несколько подходов, но наиболее корректные и распространенные включают явный вызов ПриЗаписи() и проверку СтатусВозврата().

Подход 1: Рефакторинг кода проверок в отдельную функцию (Пример из ЗиК)

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

Давайте посмотрим, как это реализовано в типовой конфигурации 1С:Зарплата и Управление Персоналом (ЗиК) версии 7.7:

Предположим, в вашем модуле документа или формы есть процедура ПриЗаписи() со следующей структурой:


// Модуль формы документа
Процедура ПриЗаписи()
    // Здесь находятся все проверки и логика
    Если ЗначениеНеЗаполнено(Контрагент) Тогда
        Сообщить("Не заполнен контрагент!");
        СтатусВозврата(0); // Отмена записи
        Возврат;
    КонецЕсли;
    // ... другие проверки ...
КонецПроцедуры

Шаг 1: Создаем новую функцию для проверок.

Мы выносим всю логику проверок в новую функцию, например, ПередЗаписьюДокумента(). Эта функция будет возвращать 1, если все проверки пройдены успешно, и 0, если запись следует отменить.


// Модуль формы документа

// ...
// (другие функции и процедуры)
// ...

Функция ПередЗаписьюДокумента()
    // Устанавливаем СтатусВозврата в 1 в начале выполнения,
    // чтобы сбросить возможное предыдущее значение 0 из других мест.
    СтатусВозврата(1); 

    Если ЗначениеНеЗаполнено(Контрагент) Тогда
        Сообщить("Не заполнен контрагент!");
        СтатусВозврата(0); // Сигнализируем о необходимости отмены
        Возврат 0; // Возвращаем 0, чтобы вызывающий код знал об отмене
    КонецЕсли;

    Если ЗначениеНеЗаполнено(СуммаДокумента) Тогда
        Сообщить("Сумма документа не заполнена!");
        СтатусВозврата(0);
        Возврат 0;
    КонецЕсли;
    
    // ... здесь могут быть другие сложные проверки ...

    // Если все проверки пройдены
    Возврат 1;
КонецФункции

Шаг 2: Модифицируем ПриЗаписи().

Теперь процедура ПриЗаписи() будет просто вызывать нашу новую функцию.


// Модуль формы документа
Процедура ПриЗаписи()
    ПередЗаписьюДокумента();
    // СтатусВозврата будет установлен внутри ПередЗаписьюДокумента(),
    // поэтому здесь дополнительно проверять ничего не нужно для интерактивной записи.
КонецПроцедуры

Шаг 3: Используем новую функцию при программной записи.

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


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

Этот подход очень гибок, так как вся логика проверок находится в одном месте, и мы можем ее переиспользовать в разных сценариях.

Подход 2: Прямой вызов ПриЗаписи() и проверка СтатусВозврата() (Наиболее распространенный)

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

Давайте посмотрим на пример кода, предложенный в обсуждении:

Предположим, ваша процедура ПриЗаписи() выглядит так (или будет выглядеть после выноса основной логики проверок):


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

Шаг 1: Вызываем ПриЗаписи() и проверяем СтатусВозврата().

В том месте, где вам нужно программно записать документ (например, перед печатью), вы выполняете следующие действия:


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

    // Явно вызываем предопределенную процедуру ПриЗаписи()
    ПриЗаписи(); 

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

Анализ этого подхода:

  1. Явный вызов ПриЗаписи(): Мы прямо вызываем процедуру, которая содержит все необходимые проверки. Это гарантирует, что вся бизнес-логика, связанная с записью, будет выполнена.
  2. Проверка СтатусВозврата(): После вызова ПриЗаписи() мы используем функцию СтатусВозврата(), чтобы узнать, отменила ли ПриЗаписи() системное действие. Если ПриЗаписи() внутри себя установила СтатусВозврата(0), то это значение будет доступно и после ее завершения, и мы сможем его проанализировать.
  3. Условное выполнение Записать(): Метод Записать() вызывается только в том случае, если ПриЗаписи() не обнаружила никаких проблем и не отменила запись (т.е., СтатусВозврата() остался равен 1).
  4. Оператор Возврат: Крайне важно использовать оператор Возврат после установки СтатусВозврата(0) и после обнаружения отмены записи в вызывающем коде. Это не только прекратит дальнейшее выполнение текущей процедуры, но и предотвратит нежелательные побочные эффекты, которые могли бы возникнуть, если бы код продолжал выполняться после отмены записи.
  5. Инициализация СтатусВозврата(1): В начале процедур, где мы проверяем СтатусВозврата(), хорошей практикой является явная установка СтатусВозврата(1). Это гарантирует, что мы начинаем проверку с "чистого листа" и не будем зависеть от статуса, установленного какими-либо предыдущими вызовами в стеке.

Разбираем работу СтатусВозврата() подробнее

Для полного понимания, рассмотрим более детально, как работает СтатусВозврата(), особенно в контексте "действия" и стека вызовов.

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

Когда мы устанавливаем СтатусВозврата(0) в процедуре ПриЗаписи(), мы фактически говорим системе: "Отмени текущее системное действие (в данном случае – запись документа) после завершения этой процедуры". Важно, что отмена происходит не мгновенно, а по завершении предопределенной процедуры. Однако, значение 0, установленное функцией СтатусВозврата(0), распространяется по всему текущему стеку вызовов. То есть, любая функция или процедура, которая была вызвана до той, где был установлен СтатусВозврата(0), при вызове СтатусВозврата() (без параметра) получит это значение 0. Это позволяет "поднять" информацию об отмене наверх по цепочке вызовов.

После того как стек вызовов "раскрутится" и выполнение вернется на самый верхний уровень (например, после завершения обработки интерактивного нажатия кнопки), значение СтатусВозврата() автоматически сбрасывается в 1, готовое для следующего системного "действия".

Таким образом, хотя установка СтатусВозврата(0) имеет смысл преимущественно в предопределенных процедурах для управления системными действиями, ее значение, доступное через функцию СтатусВозврата(), можно использовать и в пользовательских процедурах, вызванных из предопределенных, для контроля потока выполнения и принятия решений о продолжении операций.

Эволюция платформы: 1С v8.x

Интересно отметить, что в более новых версиях платформы (1С:Предприятие 8) разработчики учли эту проблему и изменили подход к обработке событий записи. В 1С 8.x для аналогичных целей используются обработчики событий объекта, такие как ПередЗаписью и ПослеЗаписи в модуле объекта. Эти обработчики вызываются при любой записи объекта, в том числе и программной, что значительно упрощает унификацию кода проверок и логики записи.

Тем не менее, для 1С v7.7 мы продолжаем использовать описанные выше подходы, которые являются стандартными и корректными решениями, обеспечивающими надежную работу наших систем.

Заключение

Мы выяснили, что процедура ПриЗаписи() в 1С v7.7 не вызывается автоматически при программной записи документа методом Записать(), поскольку она предназначена для обработки интерактивных действий пользователя. Для обхода этого ограничения и обеспечения выполнения всех необходимых проверок, мы рассмотрели два эффективных подхода:

  1. Вынесение логики проверок в отдельную функцию и вызов этой функции перед программной записью.
  2. Прямой вызов процедуры ПриЗаписи() и последующая проверка значения функции СтатусВозврата().

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

Всегда помните о важности правильного использования СтатусВозврата() и оператора Возврат для полного контроля над процессами записи и отмены действий в 1С v7.7.

← На главную