Как вернуть пользователя к редактированию цен при ошибке в документе «Установка цен номенклатуры» в 1С 8.3

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

При разработке в среде 1С:Предприятие 8.3 программисты часто сталкиваются с задачей валидации данных перед сохранением документов. Типичный пример — проверка цен на соответствие минимальной розничной цене (МРЦ) в документе УстановкаЦенНоменклатуры. Если цена ниже допустимой, систему нужно остановить, выдать предупреждение и оставить форму открытой для исправления ошибок.

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

Анализ критических ошибок: почему «костыли» опасны

Рассмотрим сначала те заблуждения и неверные решения, которые часто встречаются на форумах (и упоминаются в исходном обсуждении):

  1. Использование события «ПослеЗаписи» для проверки данных.

    Некоторые разработчики пытаются поместить проверку в событие ПослеЗаписиНаСервере или клиентское событие ПослеЗаписи и в случае ошибки вызывать исключение через ВызватьИсключение. Это крайне опасный подход.

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

  2. Заблуждение: «В событии ПередЗаписью еще нет новых цен».

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

    Почему же возникает иллюзия, что цены старые? Это происходит, если внутри процедуры проверки разработчик делает запрос к базе данных, передавая ссылку на текущий документ: ГДЕ Ссылка = &Ссылка. Запрос обращается к СУБД, где транзакция еще не зафиксирована, и считывает старые значения. Проверять данные нужно непосредственно в оперативной памяти, обходя коллекцию табличной части ЭтотОбъект.Товары.

  3. Утверждение: «В расширении недоступен модуль объекта».

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

Выяснив причины ошибок, перейдем к правильным методам реализации.

---

Вариант 1. Проверка в модуле объекта через Расширение (Рекомендуемый способ)

Проверка на уровне модуля объекта является наиболее надежной. Она сработает независимо от того, как записывается документ: интерактивно пользователем через кнопку «Провести и закрыть» или программно (например, при загрузке данных из внешней системы, обмене или обработке).

Разберем по шагам, как это реализовать:

  1. Откройте конфигуратор и добавьте документ УстановкаЦенНоменклатуры в ваше расширение.
  2. В расширении найдите заимствованный документ, нажмите по нему правой кнопкой мыши и выберите Открыть модуль объекта.
  3. Создайте процедуру-перехватчик для события ПередЗаписью с аннотацией &Перед.

Рассмотрим пример кода, который решает поставленную задачу:


&Перед("ПередЗаписью")
Процедура ИмяРасш_ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Если документ записывается в режиме загрузки (обмена), проверку обычно отключают
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    // Переменная для фиксации ошибок
    ЕстьОшибки = Ложь;
    
    // Обходим новые строки, которые сейчас находятся в памяти объекта
    Для Каждого СтрокаТЧ Из ЭтотОбъект.Товары Цикл
        
        // Получаем МРЦ (минимальную розничную цену) для товара
        МРЦ = ПолучитьМРЦНаСервере(СтрокаТЧ.Номенклатура, СтрокаТЧ.ВидЦены);
        
        Если ЗначениеЗаполнено(МРЦ) И СтрокаТЧ.Цена < МРЦ Тогда
            
            // Выводим сообщение пользователю с привязкой к конкретной строке и полю формы
            Сообщение = Новый СообщениеПользователю;
            Сообщение.Текст = Шаблон("Товар \"%1\": цена (%2 руб.) ниже минимально допустимой МРЦ (%3 руб.)!", 
                СтрокаТЧ.Номенклатура, СтрокаТЧ.Цена, МРЦ);
            
            // Задаем путь к полю на форме, чтобы платформа подсветила его красным
            Сообщение.Поле = "Товары[" + (СтрокаТЧ.НомерСтроки - 1) + "].Цена"; 
            Сообщение.УстановитьДанные(ЭтотОбъект);
            Сообщение.Сообщить();
            
            ЕстьОшибки = Истина;
            
        КонецЕсли;
        
    КонецЦикла;
    
    // Если обнаружена хотя бы одна ошибка, отменяем запись
    Если ЕстьОшибки Тогда
        Отказ = Истина; // Это остановит запись, транзакция откатится, форма не закроется
    КонецЕсли;

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

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

---

Вариант 2. Интерактивная проверка в модуле формы (Клиент-Сервер)

Если бизнес-логика требует, чтобы проверка выполнялась исключительно при интерактивной работе пользователя с формой (например, нужно задать вопрос «Вы уверены?» или дать возможность проигнорировать ошибку при определенных правах), логику переносят в модуль формы.

Проанализируем этот сценарий. Проверку необходимо инициировать на клиенте, но сами данные (МРЦ) считывать на сервере.

Заимствуем форму документа в расширение и создаем обработчик события формы ПередЗаписью:


&НаКлиенте
&Перед("ПередЗаписью")
Процедура ИмяРасш_ПередЗаписью(Отказ, ПараметрыЗаписи)
    
    // Вызываем серверный контекстный метод проверки данных формы
    Если Не ПроверитьМРЦНаФормеНаСервере() Тогда
        
        // Отменяем запись на клиенте. Данные даже не отправятся на финальную запись на сервер
        Отказ = Истина; 
        
        ПоказатьПредупреждение(, "Запись документа отменена. Пожалуйста, исправьте цены, выделенные красным цветом.");
        
    КонецЕсли;
    
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьМРЦНаСервере(Номенклатура, ВидЦены)
    // Здесь должна быть ваша бизнес-логика получения МРЦ из регистров сведений
    // Например:
    // Возврат РегистрыСведений.МинимальныеЦены.ПолучитьПоследнее(ТекущаяДата(), Новый Структура("Номенклатура, ВидЦены", Номенклатура, ВидЦены)).Цена;
    Возврат 100; // Пример возвращаемого значения
КонецФункции

&НаСервере
Функция ПроверитьМРЦНаФормеНаСервере()
    
    Успешно = Истина;
    
    // Работаем напрямую с реквизитом формы "Объект"
    Для Каждого СтрокаТЧ Из Объект.Товары Цикл
        
        МРЦ = ПолучитьМРЦНаСервере(СтрокаТЧ.Номенклатура, СтрокаТЧ.ВидЦены);
        
        Если ЗначениеЗаполнено(МРЦ) И СтрокаТЧ.Цена < МРЦ Тогда
            
            Сообщение = Новый СообщениеПользователю;
            Сообщение.Текст = Шаблон("Цена товара %1 ниже МРЦ (%2)!", СтрокаТЧ.Номенклатура, МРЦ);
            Сообщение.Поле = "Объект.Товары[" + (СтрокаТЧ.НомерСтроки - 1) + "].Цена";
            Сообщение.УстановитьДанные(Объект);
            Сообщение.Сообщить();
            
            Успешно = Ложь;
            
        КонецЕсли;
        
    КонецЦикла;
    
    Возврат Успешно;
    
КонецФункции

В данном сценарии при установке Отказ = Истина в клиентском событии ПередЗаписью транзакция записи даже не начнется. Форма гарантированно останется открытой, фокус ввода вернется пользователю, а ошибочные строки будут наглядно подсвечены.

---

Резюме и рекомендации

Подведем итоги нашей совместной работы по анализу механизмов валидации в 1С:

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

← На главную