Как и где правильно размещать проверки (контроли) в документах 1С?

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

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

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

Решение 1: Разделяем проверки по месту и времени выполнения

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

  1. Проверки на уровне формы (для удобства пользователя)

    Это самый первый рубеж контроля, который видит пользователь. Его главная задача — обеспечить быстрый отклик и не дать пользователю совершить очевидную ошибку. Такие проверки выполняются на клиенте или в форме на сервере еще до начала записи объекта в базу данных.

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

    Важно: Помните, что логика в форме — это UI (пользовательский интерфейс). Основные, критически важные бизнес-проверки не должны находиться здесь, так как их можно обойти, например, при программном создании документов или при обмене данными.

  2. Проверки при записи объекта (событие ПередЗаписью)

    Здесь мы размещаем контроли, которые отвечают за целостность самого документа как объекта. Эти проверки гарантируют, что в базу данных не запишется "бракованный" или бессмысленный документ, даже если он еще не проведен. Это идеальное место для проверок, не требующих анализа данных из регистров.

    Что здесь проверяем:

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

    Размещать этот код можно как в модуле самого объекта документа, так и в подписке на событие. Подробнее об этом выборе поговорим ниже.

  3. Проверки при проведении документа (событие ОбработкаПроведения)

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

    Что здесь проверяем:

    • Наличие достаточного количества товара на складе.
    • Состояние взаиморасчетов с контрагентом.
    • Превышение кредитного лимита.
    • Любые другие проверки, требующие анализа остатков и оборотов.

    Важный момент: Блокировать пользователю возможность записать документ (сохранить как черновик) из-за нехватки остатков — не всегда хорошая идея. Это может вызвать раздражение, ведь пользователь, возможно, просто хотел сохранить набросок документа, чтобы вернуться к нему позже. Поэтому контроль остатков почти всегда размещают именно в ОбработкеПроведения.

Решение 2: Выбираем правильный механизм — модуль объекта или подписка на событие

Теперь, когда мы определились с тем, *когда* выполнять проверку, разберем, *где* именно писать код.

Решение 3: Организуем контроль остатков правильно

Контроль отрицательных остатков — одна из самых частых задач. Рассмотрим две основные методики его реализации в обработчике ОбработкаПроведения.

  1. "Старая" методика: сначала проверка, потом запись движений

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

  2. "Новая" (рекомендуемая) методика: сначала запись, потом контроль в той же транзакции

    Этот метод более производительный. Разберем по шагам, как он работает:

    1. Документ записывает свои движения в регистры (например, делает расход).
    2. Сразу после этого, не завершая транзакцию, мы выполняем запрос, который проверяет, не образовались ли по итогам наших движений отрицательные остатки.
    3. Если отрицательные остатки найдены, мы отменяем всю операцию, установив Отказ = Истина. Транзакция откатывается, и проведения документа не происходит.

    Посмотрим на примерный код для новой методики в ОбработкеПроведения:

    
    // 1. Записываем движения без предварительных проверок
    Движения.ОстаткиТоваров.Записывать = Истина;
    Для Каждого Стр Из Товары Цикл
        Движение = Движения.ОстаткиТоваров.Добавить();
        Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
        Движение.Период = Дата;
        Движение.Номенклатура = Стр.Номенклатура;
        Движение.Склад = Склад;
        Движение.Количество = Стр.Количество;
    КонецЦикла;
    
    // 2. Выполняем контроль остатков после записи движений
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ОстаткиТоваровОстатки.Номенклатура,
        |    ОстаткиТоваровОстатки.КоличествоОстаток
        |ИЗ
        |    РегистрНакопления.ОстаткиТоваров.Остатки(&МоментВремени, Номенклатура В (&СписокНоменклатуры)) КАК ОстаткиТоваровОстатки
        |ГДЕ
        |    ОстаткиТоваровОстатки.КоличествоОстаток < 0";
    
    Запрос.УстановитьПараметр("МоментВремени", МоментВремени());
    Запрос.УстановитьПараметр("СписокНоменклатуры", Товары.ВыгрузитьКолонку("Номенклатура"));
    
    Результат = Запрос.Выполнить();
    Если НЕ Результат.Пустой() Тогда
        // 3. Если есть отрицательные остатки, отменяем проведение
        Отказ = Истина;
        Сообщить("Обнаружены отрицательные остатки! Проведение отменено.");
    КонецЕсли;
    

Особый случай: как управлять проверками?

В реальной жизни часто возникают ситуации, когда проверки нужно временно отключить. Самый яркий пример — загрузка данных при обмене.

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

Посмотрим на пример:


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

    Если ОбменДанными.Загрузка Тогда
        Возврат; // Ничего не проверяем при обмене
    КонецЕсли;

    // ... здесь идет ваш код проверок ...
    Если НЕ ЗначениеЗаполнено(Контрагент) Тогда
        Сообщить("Не заполнен контрагент!");
        Отказ = Истина;
    КонецЕсли;

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

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

← К списку